MySQL查询优化器源码分析
为了重新实现一套可用的SQL优化组件,重新梳理了一遍MySQL查询优化器相关的代码。
MySQL查询优化器的入口函数为handle_query(THD*, LEX*, Query_result*, unsigned long long, unsigned long long)
,其实对于select语句来讲,无论是Explain操作还是真正的执行SQL语句,入口都一样,下面开始从这个函数逐步进入Optimizer。由于某些原因,代码基于MySQL-5.7.18。
handle_query(THD*, LEX*, Query_result*, unsigned long long, unsigned long long)
为了简化代码,提取lex中的unit(unit可以简单认为是一个经过查询解析处理后的,可以完整表示用户所输入的SQL的查询解析树)。
SELECT_LEX_UNIT *const unit= lex->unit;
提取查询解析树中的第一个查询块,可以理解为你的SQL中包含了多个union,现在提起的是第一个层级的第一个sql。
SELECT_LEX *const select= unit->first_select();
随后是两个assert操作,主要程序员自己为了防止脑袋短路,写一堆类似于0==1的代码。
DBUG_ASSERT(thd == unit->thd);
DBUG_ASSERT(!unit->is_prepared() && !unit->is_optimized() &&
!unit->is_executed());
检验是否是single_query
const bool single_query= unit->is_simple();
- single_query代表什么呢?可以展开unit成员函数is_simple()自行查阅.比较常见的区别就是用户SQL语句中是否包含了union操作。
然后根据是否是single_query,通过不同的方式进入查询优化的prepare阶段,并且此时线程进入init状态,也就是show processlist中的init.
先看如果是single_query的进入方式,代码如下,解释在后面
if (single_query)
{
sql_print_information("single_query = true");
unit->set_limit(unit->global_parameters());
select->context.resolve_in_select_list= true;
select->set_query_result(result);
select->make_active_options(added_options, removed_options);
select->fields_list= select->item_list;
if (select->prepare(thd))
goto err;
unit->set_prepared();
}
- 日志打印是后加的,用来观察查询优化器
- 调用unit->set_limit来设置查询的limit,包括开始偏移量和结束偏移量。
- select->set_query_result(result) 设置查询的结果,现在只是一个空的result
- select->make_active_options(added_options, removed_options);其实在查询操作中,这里什么都没做。
- select->fields_list= select->item_list; 设置查询中的表达式
- select->prepare(thd) 进入prepare阶段
- unit->set_prepared();如果没有错误,则设置{ prepared= true; }。表示此SQL已经被prepared了,猜测后面会有assert验证。
如果不是single_query,则如下
sql_print_information("single_query = false");
if (unit->prepare(thd, result, SELECT_NO_UNLOCK | added_options,
removed_options))
goto err;
可以看到其中的主要不同在于如果是single_query,则直接调用unit->first_select()中的prepare,否则调用unit->prepare();
但其实,进入unit->prepare()看下就知道,只是个壳子而已. 如下:
for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select())
{
sl->set_query_result(tmp_result);
sl->make_active_options(added_options | SELECT_NO_UNLOCK, removed_options);
sl->field