Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1690160
  • 博文数量: 107
  • 博客积分: 1715
  • 博客等级: 上尉
  • 技术积分: 3168
  • 用 户 组: 普通用户
  • 注册时间: 2012-04-18 18:42
个人简介

阿里巴巴DBA,原去哪儿网DBA。专注于MySQL源码研究、DBA运维、CGroup虚拟化及Linux Kernel源码研究等。 github:https://github.com/HengWang/ Email:king_wangheng@163.com 微博 :@王恒-Henry QQ :506437736

文章分类

全部博文(107)

文章存档

2014年(2)

2013年(38)

2012年(67)

分类: Mysql/postgreSQL

2012-08-25 14:41:10

紧接着之前的《MySQL 查询优化器实验分析(四)》中的内容,以下对测试过程进行详细分析。

2.5 LEFT JOIN查询

       该测试主要用于测试LEFT JOINJOIN的处理逻辑上的差异,具体查询处理逻辑如下所示:

 

JOIN:prepare阶段

setup_tables()2.1测试。

setup_fields()2.1测试。

setup_conds()2.4测试。

JOIN:optimize阶段

simplify_joins()类似2.4测试。不同之处在于由于LEFT JOIN 使用的数据表不能为 NULL表,这是由是否有where条件过滤决定的。所以该过程会将LEFT JOIN外链接查询转化为多表联合查询操作,从而忽略LEFT JOIN的链接操作。

optimize_cond()2.1测试。

make_join_statistics()2.4测试。

choose_plan()2.1测试。

greedy_search()2.1测试。

best_extension_by_limited_search()2.4测试。

get_best_combination()2.4测试。

JOIN:exec阶段

以下2.4测试。

 

对应的查询计划如下所示:

 

id

select_type

table

type

possible_keys

key

key_len

ref

rows

Extra

1

SIMPLE

course

ALL

PRIMARY

NULL

NULL

NULL

20

Using Where

1

SIMPLE

std_cur

ref

PRIMARY,std_cur_ibfk_2

std_cur_ibfk_2

4

test.course.cur_id

13

 

1

SIMPLE

student

eq_ref

PRIMARY

PRIMARY

4

test.std_cur.std_id

1

 

 

       通过以上测试发现,除了在simplify_joins()处有略微的不同之外,其他处理逻辑和查询计划与测试2.4都是一样的。而LEFT JOIN从理论来说,会将左边的表的进行全表扫描,而右边的表中如果没有匹配的记录时,会用NULL值填充。然后从查询结果来看,查询的记录并非student表的所有记录。并且从查询计划来看,student表的查询类型不是ALL

通过查看simplify_joins()函数的注释和源码实现发现,OUTER JOIN可以转化为INNER JOIN,转化的条件与used_tablesnot_null_tables的值有关。而not_null_tables的值是根据where条件决定的。如果有where条件过滤,那么LEFT JOIN会被转化为INNER JOIN查询。更进一步的研究,将作为单独的问题进行详细的研究和测试。

 

2.6 Natural JOIN查询

       Natural JOIN的查询处理逻辑如下所示:

 

JOIN:prepare阶段

setup_tables()类似2.1测试,不同之处在于调用setup_natural_join_row_types()函数将Natural JOIN查询转化为JOIN ON查询。转化过程为:找到共同的字段名,作为ON的条件进行链接查询。

setup_fields()2.1测试。

setup_conds()2.3测试。

JOIN:optimize阶段

simplify_joins()2.3测试。

optimize_cond()2.1测试。

make_join_statistics()2.3测试。

choose_plan()2.1测试。

greedy_search()2.1测试。

best_extension_by_limited_search()2.3测试。

get_best_combination()2.3测试。

JOIN:exec阶段

以下2.3测试。

 

对应的查询计划如下所示:

 

id

select_type

table

type

possible_keys

key

key_len

ref

rows

Extra

1

SIMPLE

course

const

PRIMARY

PRIMARY

4

const

1

 

1

SIMPLE

std_cur

ref

PRIMARY,std_cur_ibfk_2

std_cur_ibfk_2

4

const

26

 

1

SIMPLE

student

eq_ref

PRIMARY

PRIMARY

4

test.std_cur.std_id

1

 

 

       由以上查询处理逻辑和查询计划可以看出,Natural JOIN查询处理与JOIN ON的处理方式类似。唯一不同之处在set_tables()处理阶段,调用setup_natural_join_row_types()函数将Natural JOIN查询转化为JOIN ON查询。从官方文档中可以知道,Natural JOIN也说明了该查询的处理逻辑[1]。应用中,Natural JOIN查询有其局限性,必须有相同的字段名,才能使用。因此,在查询处理中,应避免使用不明确的字段进行联合查询,使用用明确的字段进行链接,不但能够获取到正确的查询结果,而且可以避免MySQL额外的查询处理逻辑。

 

2.7 Straight_JOIN查询

       STRAIGHT_JOIN查询是是 MySQL 对标准 SQL 的扩展,用于在多表查询时指定表载入的顺序。具体的查询处理逻辑如下所示:

 

JOIN:prepare阶段

setup_tables()2.1测试,

setup_fields()2.1测试。

setup_conds()2.3测试。

JOIN:optimize阶段

simplify_joins()类似2.3测试,不同之处在于根据STRAIGHT_JOIN的条件,为数据表建立依赖关系,也就是说右部的表依赖于左部的表。同样,转化为JOIN的查询处理。

optimize_cond()2.1测试。

make_join_statistics()2.3测试。

choose_plan()2.1测试。

greedy_search()2.1测试。

best_extension_by_limited_search()类似2.3测试,不同之处在于根据表的依赖关系,查找最优的查询计划。由于右部的表依赖于左部的表,因此在组合查找最优路径时,右部的表必须始终在左部的表的后面。

get_best_combination()2.3测试。

JOIN:exec阶段

以下同2.3测试。

 

由于STRAIGHT_JOIN的查询与表的顺序有关系,为了体现这一特点,测试两条不同表顺序的SQL语句进行比较查询计划的不同。

第一条SQL查询对应的查询计划如下所示:

 

id

select_type

table

type

possible_keys

key

key_len

ref

rows

Extra

1

SIMPLE

course

ALL

PRIMARY

NULL

NULL

NULL

20

Using Where

1

SIMPLE

std_cur

ref

PRIMARY,std_cur_ibfk_2

std_cur_ibfk_2

4

test.course.cur_id

13

 

1

SIMPLE

student

eq_ref

PRIMARY

PRIMARY

4

test.std_cur.std_id

1

 

 

第二条SQL查询对应的查询计划如下所示:

 

id

select_type

table

type

possible_keys

key

key_len

ref

rows

Extra

1

SIMPLE

student

ALL

PRIMARY

NULL

NULL

NULL

31

 

1

SIMPLE

std_cur

ref

PRIMARY,std_cur_ibfk_2

std_cur_ibfk_2

4

test.course.std_id

10

 

1

SIMPLE

course

eq_ref

PRIMARY

PRIMARY

4

test.std_cur.cur_id

1

Using Where

 

       通过以上查询处理逻辑来看,STRAIGHT_JOINJOIN查询类似,不同之处在于将STRAIGHT_JOIN转化为表的依赖关系,从而限制查找最优查询计划过程中表的组合方式,而没有直接调用函数optimize_straight_join() (sql\sql_select.cc:5108)来取代greedy_search()函数的优化。

       此外,从查询计划来看,第一个SQL查询没有按照指定的表的顺序执行,这是因为指定的student表和course表没有关联关系,转化为JOIN查询处理,通过查询优化器计算的代价来决定student加载的顺序。而course表与std_cur表存在关联关系,所以会按照course表首先加载,std_cur表后加载。而从第二个SQL查询来看,完全按照指定表的顺序进行加载。

 

2.8 子查询

       SQL查询中有子查询时,查询处理逻辑就远远不是以上处理逻辑了,查询处理过程的入口函数为execute_sqlcom_select()(sql\sql_parse.cc:4530)。因为从该函数开始,就已经对子查询进行处理。当然之前的查询处理也都从该函数入口,但是之前的查询在该过程中并没有特殊的处理,并且主要分析查询优化器的处理逻辑,因此没有就该部分做过多的分析。

       具体的查询处理逻辑如下所示:

 

execute_sqlcom_select()查询入口函数。由于子查询中的表tmp不是实际的表,因此,会首先对子查询进行处理。首先调用open_and_lock_tables()函数打开查询中的所有表并添加锁。其次调用handle_select()函数处理查询处理。

open_and_lock_tables()该函数用于打开SQL查询中的表,并添加锁。此外,还会处理驱动表。调用open_tables()函数打开所有的表,lock_tables()函数为所有表添加锁,调用mysql_handle_derived()函数处理驱动表。而对子查询来说,特殊的处理逻辑为调用mysql_handle_derived()处理驱动表的过程。(sql\sql_base.cc:5494

mysql_handle_derived()用于处理驱动表,实际处理过程根据给定的函数指针,执行对应的处理阶段。首先调用mysql_derived_prepare()函数中的处理逻辑,准备子查询的处理逻辑;其次调用mysql_derived_filling()函数处理子查询中的优化和执行的处理逻辑;最后调用mysql_derived_cleanup()函数清理子查询使用的资源。(sql\sql_derived.cc:46

mysql_derived_prepare()用于准备子查询的处理逻辑过程。从查询处理过程来看,MySQL将子查询作为一个查询子单元(unit)来看,共用了union的查询逻辑处理过程。调用st_select_lex_unit::prepare()函数(sql\sql_union.cc:172),执行JOIN::prepare阶段,查询处理逻辑类似2.1测试,不同之处是group条件的处理过程,类似测试1.9。调用select_union::create_result_table()函数(sql\sql_union.cc:117)创建临时表,调用create_tmp_table()函数执行具体的创建过程。(sql\sql_derived.cc:135

mysql_derived_filling()处理子查询优化和执行的处理逻辑。调用mysql_select()函数实际进行查询的处理逻辑,由于已经执行了prepare过程。因此,该过程主要处理JOIN::optimize阶段,优化过程的处理逻辑类似2.1测试,对group条件的处理过程(需要创建临时表,存储临时结果),类似测试1.9。处理JOIN::exec阶段,执行过程的处理逻辑类似2.1测试,不同之处在于查询结果存储到prepare阶段创建的临时表中。(sql\sql_derived.cc:264

mysql_derived_cleanup()清理子查询使用的资源。调用st_select_lex_unit::cleanup()函数进行具体的清理工作。(sql\sql_derived.cc:321

handle_select()主查询过程的实际处理逻辑,查询处理过程类似2.1测试多表联合查询处理方式,不同之处是联合查询四个表,其中tmp为子查询处理过程中得到的临时表。

 

对应的查询计划如下所示:

 

id

select_type

table

type

possible_keys

key

key_len

ref

rows

Extra

1

PRIMARY

ALL

NULL

NULL

NULL

NULL

5

Using temporary; Using filesort

1

PRIMARY

course

ALL

PRIMARY

NULL

NULL

NULL

20

Using join buffer

1

PRIMARY

std_cur

ref

PRIMARY,std_cur_ibfk_2

std_cur_ibfk_2

4

test.std_cur.cur_id

13

Using where

1

PRIMARY

student

eq_ref

PRIMARY

PRIMARY

4

test.std_cur.std_id

1

Using where

2

DERIVED

student

ALL

NULL

NULL

NULL

NULL

31

Using temporary; Using filesort

2

DERIVED

std_cur

ref

PRIMARY

PRIMARY

4

test.std_cur.std_id

10

 

 

       从以上查询处理逻辑来看,子查询的处理逻辑与之前的处理有所不同,其入口函数的层次有所提升,并且处理的流程也发生了变化。对子查询来说,首先根据查询条件和查询的表获得子查询语句中的结果集,该过程的查询处理作为union中的一个单元来处理。处理逻辑与单独的查询处理过程类似,最后将查询的结果集存储到临时表中。然后执行多表联合查询,获得最终的查询结果。

       从查询计划来看,特别之处是select_type的值为PRIMARYDERIVED,其中PRIMARY表示为最外的查询;DERIVED表示驱动表查询,依赖于外部的查询。因此,select_typeDERIVED的是查询获取驱动表的查询计划,select_typePRIMARY的是主查询的查询计划。

    接下来的内容将在接下来的《MySQL 查询优化器实验分析(六)》文档中给出。
阅读(3561) | 评论(2) | 转发(0) |
给主人留下些什么吧!~~

king_wangheng2014-05-02 11:53:57

hi121073215:你好..我想问下mysql的底层源码可以在哪里找到呢?

MySQL是开源的,下载源码包即可!!!

回复 | 举报

hi1210732152014-04-28 12:12:23

你好..我想问下mysql的底层源码可以在哪里找到呢?