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