MySQL分析器与优化器

>>>>分析器
由词法扫描器与语法分析器构成

词法扫描器
1、MySQL实现自己的词法扫描器
2、人工实现信息编排以便优化器获取信息,自动生成代码无法做到
3、实现上下文关联的token意义解析
4、关键字列表symbols[]在sql/sql_lex.h,功能名称列表sql_functions[]在sql/sql_lex.h
5、所有关键词表gen_lex_hash(sql/gen_lex_hash.cc)
6、词法扫描器在sql/sql_lex.cc
7、词法分析器的入口yylex()(sql/sql_lex.cc),依赖GNU Bison库。

语法分析器
使用语法生成器GNU Bison库。想更好了解MySQL语法分析阶段或者修改MySQL语法,需要对Bison有更深入了解
MySQL规则定义在sql/sql_yacc.yy,Bison使用它生成sql/sql_yacc.cc,语法规则分析入口yyparse().

语法树
语法分析结果得出语法树,不难想象,复杂的语法结构需要一个足够复杂有效地结构体来存储执行SQL语句阶段所需要的一切信息。
语法树以类型LEX对象体现,该类型实际上结构体st_lex(sql/sql_lex.h)的同名。
该结构体中至关重要的两个成员变量:
enum_sql_command sql_command;
SELECT_LEX select_lex;
sql_command表示了当前SQL语句的类型,select、update、delete或者其他类型语句,mysql_execute_command()(sql/sql_parse.cc)
中根据语句类型来执行流程的处理函数。
select_lex属于结构体st_select_lex类型(sql/sql_lex.h),该结构体保存了查询语句相关联的多种细节信息,如where子句、涉及表、
涉及列、优化建议、对子查询的SELECT_LEX实例的交叉引用、ORDER BY/GROUP BY/HAVING表达式、以及其他细节。
st_select_lex类型的关键成员Item* where,保存了where子句树的根节点,优化器需要的大量信息都是从where子句中萃取。

Item结构具备一些以val_开头的函数,函数名称的其他部分依赖于函数的返回类型,如val_int()返回int类型。
优化器使用st_select_lex类型的where变量来构建对记录组合的过滤表达式,过滤表达式通过调用Item::val_int()来过滤结果,
如果返回1,说明该记录符合约束条件,否则该记录不符合会被丢弃。

如果优化器不对过滤表达式进行改进的话,那么表达式完全与where子句对应。
优化器重写表达式减少计算量、使用更好的索引值。


>>>>优化器

考虑A join B join C,并存在where子句情形
原生处理逻辑:组合A,B,C三表,每个组合再过滤where表达式,这种情况需要计算A*B*C种组合,耗时异常
可能优化步骤,根据where子句筛选符合条件记录,再通过符合条件记录查找其他表信息,条件列都加上索引,效率提高非常。

MySQL的优化器有几个重要任务:
1、选择最合适的索引
2、选择表扫还是走索引
3、选择表关联顺序
4、优化where子句
5、排除管理中无用表
6、决定order by和group by是否走索引
7、尝试使用inner join替换outer join
8、简化子查询,决定结果缓存
9、合并试图

优化算法基础
对于优化器来说,每个查询都是广义的join,甚至从一个表里查询记录属于退化join处理,使用同样的数据结构和算法
处理所有的join操作,即便是一个表的情况

优化器处理的基本单位便是一个join,代码级别上,join与类JOIN(sql/sql_select.h)对应。
join通过调用mysql_select()(sql/sql_select.cc)启动执行。


执行过程分为两部分: 决定最优join顺序、嵌套循环执行join
join本质上是表子集的笛卡尔积。每个子集可以通过根据独个键查找、键范围查询、索引全扫、表扫来获取。还可能使用where子句。
优化器选择记录访问方式和安排最优表顺序,确保操作消耗最小化。

查询优化方式分为两部分:
1、根据提供的表join顺序,找出每个表访问的最佳方式
     best_access_path()(sql/sql_select.cc),决定读索引,扫表,扫索引。
     索引访问的话,决定如何使用索引,eq_ref或者ref甚至range方式。
     best_access_path() 根据旧的执行计划来计算访问方式。
2、找出最优的join顺序,两种方式来决定:全量搜索与贪婪搜索
     全量搜索:find_best()(sql/sql_select.cc),计算所有表可能组合得出最优结果,时间消耗大。
     贪婪搜索:greedy_search()(sql/sql_select.cc),计算optimizer_search_depth个表的所有组合,得出最优结果。
相关的代码:make_join_statistics(), choose_plan(), optimize_key_use(),best_access_path(),
get_best_combination(),create_ref_for_key(),find_best(),greedy_search()定义于sql/sql_select.cc


EXPLAIN输出信息
EXPLAIN输出信息本质上是对JOIN类的可读性输出,JOIN类记录了查询执行计划信息,EXPLAIN输出域代码的对应处:
(EXPLAIN输出域:代码相关字段:意义)
id:select_lex->select_number:查询ID
select_type:select_lex->type:Select Type,Simple或者其他
table:join_tab[k-1].table->alias或者join_tab[k-1].table->derived_select_number:查询中表的输出
type:join_tab[k-1].type:Record access types,从表中获取记录的方法
possible_keys:join_tab[k-1].keys:可用索引列表
key:join_tab[k-1].ref.key或者join_tab[k-1].index或者oin_tab[k-1].select->quick->index或者join_tab[k-1].table->key_info:使用的索引
key_len:KEY结构体的key_length:使用索引长度,可能是索引前缀
ref:join_tab[k-1].ref.key_copy:该次查询与相关其他表字段列表
rows:join_tab[k-1].best_positions.records_read:估计结果集数目
Extra:JOIN结构信息收集:注释


查询类型:
EXPLAIN输出的select_type域取值
SIMPLE:没有子查询或者UNION的查询
PRIMARY:最外层的查询或者UNION的第一个select
UNION:UNION查询并且不是第一个select
DEPENDENT UNION:类似UNION,但子句依赖除外。何为子句依赖:每个外部查询都需要触发子句的重新执行。
UNION RESULT:union结果合并
SUBQUERY:子句非依赖查询
DEPENDENT SUBQUERY:子句依赖查询
DERIVED:查询产生继承表。何为继承表:由查询结果产生的表


记录访问类型
EXPLAIN输出的select_type域取值
system:特殊情况,当表只有一行记录
const:表中至少出现一个匹配记录,查询启动开始后执行一次。表存在唯一索引键并且where子句提供了一个值查询。
eq_ref:与const类似。提供的值不是固定常数,而是由其他表提供的值。仅获取一行记录。唯一索引值
ref:与eq_ref类似。提供常数值作为索引查询条件。但是可能获取多行记录。不是唯一索引值或者使用前缀索引时出现。
ALL:全表扫。对应列没有索引可用。
range:范围查询
index:整个索引都被扫描。发生在没有提供索引匹配值时。覆盖索引情况下比较有效。
fulltext:全文检索
ref_or_null:与ref差别是意味着可能出现NULL情况
unique_subquery:当子查询select出来的列是唯一值时
index_subquery:当子查询select出来的列不是唯一值时
index_merge:双索引通过OR查询之后合并结果

Extra field
EXPLAIN的Extra field输出,主要几项:
Using where:where子句条件过滤出来的
Using index:覆盖索引,当需要的列数据都包含在索引中,扫描索引即可。
Using index for group-by:仅在索引上读取第一个或者最后一个记录便可满足需求。
     除了min()和max()之外,没有其他聚类函数;涉及到一个表;覆盖索引;
Using filesort:使用外部排序
Using temporary:需要创建临时表。如GROUP BY非索引列
Distinct:仅需要distinct列数据,选择唯一索引与where子句匹配。无需去重


范围查询优化
MySQL特意加入了模块range optimizer来处理索引上的范围查找,代码实现于sql/opt_range.h(cc)的SQL_SELECT::test_quick_select()
范围优化支持以下几种:
1、Range: 类QUICK_RANGE_SELECT_GEOM、QUICK_RANGE_SELECT
2、Index_merge: 类QUICK_INDEX_MERGE_SELECT
3、Range_desc: 类QUICK_SELECT_DESC
4、Fulltext: 类FT_SELECT
5、ROR_intersect: 类QUICK_ROR_INTERSECT_SELECT
6、ROR_union: 类QUICK_ROR_UNION_SELECT.
7、Group_min_max: 类QUICK_GROUP_MIN_MAX_SELECT

子查询优化
1、相对比较少的优化
2、常量替换计算

优化器核心类与结构体
1、JOIN: 每次select查询认为一个join实例。定义于sql/sql_select.h
2、JOIN_TAB: 记录每个表实例与优化器的信息。定义于sql/sql_select.h
3、select_result: 不同处理查询结果方式的类的基类。定义于sql/sql_class.h



查询语法树
语法树的基础类是st_select_lex_node(sql/sql_lex.h),作为st_select_lex_unit和st_select_lex的基类。
st_select_lex_unit表示UNION的描述符
st_select_lex表示单独一个select

select执行路径
1、语法分析器初始化检查后,handle_select()(sql/sql_select.cc)实现执行流程控制。
2、union操作调用mysql_union(),而单个select函数调用mysql_select()
3、单个select处理流程分为:
     *JOIN::prepare()
     *JOIN::optimize()
     *JOIN::exec()
     *JOIN::cleanup()
     如果需要重复调用JOIN::exec(),需要重新初始化JOIN::reinit()
4、JOIN::prepare()做一系列初始化
     *setup_tales()(sql/sql_base.cc)
     *setup_wild()(sql/sql_base.cc)
     *setup_fields()(sql/sql_base.cc)
     *setup_conds()(sql/sql_base.cc)
     *setup_order()/setup_group()
5、JOIN::optimize() 优化重构查询,制定执行计划
     *simplify_joins()(sql/sql_select.cc)
     *optimize_cond()(sql/sql_base.cc)
     *opt_sum_query()(sql/opt_sum.cc)
     *make_join_statistics() -- 创建执行计划
     *substitute_for_best_equal_field()
     *remove_const()
     *make_join_readinfo()
     *尝试用常量替换简单子查询
     *screate_tmp_table()
6、make_join_statistics()
     *初始化JOIN类,分配内存
     *检测表之间关系,初始化从属位图
     *update_ref_and_keys() —— 标识哪个索引可以使用
     *检测固定表(不会匹配超过一行记录),建立固定的表位图
     *get_quick_record_count() —— 初始化每个表的统计信息,索引选择基数等
     *choose_plan() —— 尝试所有的组合来预定深度,并选择最合适的一个。
7、JOIN::exec() 执行查询。依赖select_result的类型,结果可能发送到客户端,或者临时表或者内部处理模块。
     *select_result::prepare2()
     *select_describe() 检测该查询是否是EXPLAIN
     *无表式select处理
     *return_zero_rows()式无结果返回处理
     *根据distinct/group by/order by查询时,create_tmp_table()创建临时表
     *do_select() 嵌套循环处理
8、JOIN::cleanup()


你可能感兴趣的:(MySQL)