目的
基于之前出现的主从库分别执行相同语句,查询计划和执行时间不同的问题。通过对源代码跟踪和调试,对MySQL的查询优化器进行分析并编写文档,为开发人员和数据库管理人员提供查询SQL语句的建议。
基础
MySQL的设计架构在官方文档中给出,如下图所示。该图的具体描述和讲解,请参考官方文档或地址:http://dev.mysql.com/doc/refman/5.1/en/pluggable-storage-overview.html。
图中Optimizer部分为本文研究的重点,主要对Parser解析之后的SQL,根据统计的数据,对访问代价进行权衡,制定执行计划。查询优化器是MySQL中比较活跃的一部分,代码会经常变动。但整体而言,对查询优化器整体把握和理解之后,其他的版本也基本可以轻松理解。以下内容将通过对MySQL 5.5.20版本的源码进行分析,供大家参考。
代码分析
1、Parser阶段
查询优化器相关的代码主要在sql/sql_select.cc文件中,而在执行该部分代码时,需要对之前的内容做简要说明。本文从Parser阶段case SQLCOM_SELECT:中调用execute_sqlcom_select()函数开始(参看源码sql/sql_parse.cc:2124行),该函数是实际的sql执行阶段,而sql真正执行需要按照一定的规则去执行。因此,需要先制定一个执行计划。但制定计划不能随便制定,需要有一定的依据。并且不能保证所有人写的SQL都是最优的,也就不能根据SQL就简单制定一个规则。于是,出现了SQL Optimizer,该部分不仅对SQL进行了优化,并且根据优化的结果制定查询的执行计划。最终根据执行计划执行,得到数据结果并返回。从代码看,execute_sqlcom_select()函数做了以上的所有工作。
以下将从execute_sqlcom_select()函数开始,逐层次的分析MySQL的查询优化是如何实现的。
2、Optimizer阶段
execute_sqlcom_select()函数中,调用了handle_select()函数,而该函数正是处理SQL查询的“入口函数”。
2.1 函数调用层次
首先,将主要的函数调用层次列出,函数层次以之前的“|”标示,以“>”标示进入函数,以“<”标示退出函数,红色标示为递归调用的函数,绿色和蓝色标示为互斥操作。
| | | | >handle_select | | | | | >mysql_select/mysql_union | | | | | | >JOIN::prepare | | | | | | | >setup_tables | | | | | | | <setup_tables | | | | | | | >setup_fields | | | | | | | <setup_fields | | | | | | | >setup_without_group | | | | | | | | >setup_conds | | | | | | | | <setup_conds | | | | | | | | >setup_order | | | | | | | | <setup_order | | | | | | | | >setup_group | | | | | | | | <setup_group | | | | | | | <setup_without_group | | | | | | | >setup_procedure | | | | | | | <setup_procedure | | | | | | <JOIN::prepare | | | | | | >JOIN::optimize | | | | | | | >simplify_joins | | | | | | | <simplify_joins | | | | | | | >build_bitmap_for_nested_joins | | | | | | | <build_bitmap_for_nested_joins | | | | | | | >optimize_cond | | | | | | | <optimize_cond | | | | | | | >prune_partitions | | | | | | | <prune_partitions | | | | | | | >make_join_statistics | | | | | | | | >make_select | | | | | | | | <make_select | | | | | | | | >get_quick_record_count | | | | | | | | | >SQL_SELECT::test_quick_select | | | | | | | | | | >get_mm_tree | | | | | | | | | | <get_mm_tree | | | | | | | | | | >get_best_group_min_max | | | | | | | | | | <get_best_group_min_max | | | | | | | | | | >get_key_scans_params | | | | | | | | | | <get_key_scans_params | | | | | | | | | | >get_best_ror_intersect | | | | | | | | | | <get_best_ror_intersect | | | | | | | | | | >get_best_disjunct_quick | | | | | | | | | | <get_best_disjunct_quick | | | | | | | | | | >TRP_RANGE::make_quick | | | | | | | | | | <TRP_RANGE::make_quick | | | | | | | | | <SQL_SELECT::test_quick_select | | | | | | | | <get_quick_record_count | | | | | | | | >choose_plan | | | | | | | | | >optimize_straight_join | | | | | | | | | | >best_access_path |