当前位置: 首页 > article >正文

MySQL 8 C++ 源码解析:EXPLAIN 实现机制

在 MySQL 中,EXPLAIN 是分析查询执行计划的核心工具。其源码实现涉及 查询解析优化器决策 和 执行计划生成 等多个模块。本文基于 MySQL 8.1.0 源码,深入解析 EXPLAIN 的实现逻辑。


1. 整体架构与入口

(1) 命令分发:mysql_execute_command

所有 SQL 命令的入口函数为 mysql_execute_command(位于 sql/sql_parse.cc)。EXPLAIN 作为 DML 的扩展命令,通过 lex->sql_command 标识类型:

 
switch (lex->sql_command) {
  case SQLCOM_EXPLAIN:      // 标准 EXPLAIN SELECT/UPDATE/DELETE
  case SQLCOM_EXPLAIN_OTHER:// EXPLAIN FOR CONNECTION 或特殊格式
    // 调用 DML 执行逻辑
    res = lex->m_sql_cmd->execute(thd);
    break;
  // 其他命令分支...
}
  • 关键逻辑EXPLAIN 被归类为 SQLCOM_EXPLAIN 或 SQLCOM_EXPLAIN_OTHER,触发 DML 执行路径。

2. DML 执行入口:Sql_cmd_dml::execute_inner

(1) 优化与执行计划生成

EXPLAIN 的核心逻辑在 Sql_cmd_dml::execute_inner(位于 sql/sql_select.cc):

 
bool Sql_cmd_dml::execute_inner(THD *thd) {
  Query_expression *unit = lex->unit;
  
  // 1. 优化查询表达式(生成 AccessPath)
  if (unit->optimize(thd, /*materialize_destination=*/nullptr,
                     /*create_iterators=*/true, /*finalize_access_paths=*/true))
    return true;

  // 2. 处理 EXPLAIN 逻辑
  if (lex->is_explain()) {
    if (explain_query(thd, thd, unit)) return true;
  }
  return false;
}
  • 核心步骤
    1. unit->optimize:调用优化器生成执行计划(AccessPath 结构)。
    2. explain_query:根据优化结果生成可读的执行计划输出。

3. 执行计划生成:explain_query 函数

(1) 输出格式处理

explain_query(位于 sql/opt_explain.cc)负责将优化器生成的 AccessPath 转换为文本、JSON 或树形格式:

 
bool explain_query(THD *explain_thd, const THD *query_thd, Query_expression *unit) {
  // 1. 判断是否使用迭代器模式(如 JSON/TREE 格式)
  if (lex->explain_format->is_iterator_based()) {
    return ExplainIterator(explain_thd, query_thd, unit);
  }

  // 2. 传统格式(TRADITIONAL)
  return mysql_explain_query_expression(explain_thd, query_thd, unit);
}
  • 分支逻辑
    • 迭代器模式:用于 FORMAT=JSON 或 FORMAT=TREE,调用 ExplainIterator
    • 传统模式:生成表格形式的输出,调用 mysql_explain_query_expression

4. 优化器交互:Query_expression::optimize

(1) 优化流程

Query_expression::optimize(位于 sql/sql_union.cc)负责对查询表达式(如 UNION)进行优化:

 
bool Query_expression::optimize(THD *thd, ...) {
  // 1. 优化每个查询块(Query_block)
  for (Query_block *query_block = first_query_block(); query_block; query_block = query_block->next_query_block()) {
    query_block->optimize(thd, finalize_access_paths);
  }

  // 2. 处理集合操作(UNION/INTERSECT)
  if (!is_simple()) optimize_set_operand(thd, this, query_term());

  // 3. 生成访问路径(AccessPath)
  create_access_paths(thd);
}
  • 核心操作
    1. 单查询块优化:为每个 SELECT 子句生成最优执行路径。
    2. 集合操作处理:优化 UNION 等操作的排序、去重逻辑。
    3. 访问路径生成:将优化结果封装为 AccessPath 结构,供执行引擎使用。

5. 特殊场景处理

(1) EXPLAIN ANALYZE

当启用 EXPLAIN ANALYZE 时,源码会 实际执行查询 并收集性能数据:

 
if (lex->is_explain_analyze) {
  Query_result_null null_result;
  unit->set_query_result(&null_result);  // 抑制结果输出
  unit->execute(thd);                    // 实际执行查询
  ExplainIterator(explain_thd, query_thd, unit); // 生成带统计信息的计划
}
  • 关键逻辑:通过执行查询获取精确的行数、耗时等指标。
(2) 二级引擎支持

若查询使用二级引擎(如 HeatWave),生成警告并调整执行计划:

 
if (SecondaryEngineHandlerton(query_thd) != nullptr) {
  push_warning(explain_thd, "Query may use secondary engine");
  return Explain_secondary_engine(...);  // 生成引擎特定的计划
}

6. 执行计划输出示例

(1) 传统格式(TRADITIONAL)

通过 mysql_explain_query_expression 生成表格:

 
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra          |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
| 1  | SIMPLE      | t     | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 1000 | 10.00    | Using where    |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
(2) JSON 格式

通过 ExplainIterator 生成结构化 JSON:

 
{
  "query_block": {
    "select_id": 1,
    "cost_info": {"query_cost": "1.20"},
    "table": {
      "table_name": "t",
      "access_type": "ALL",
      "rows_examined_per_scan": 1000
    }
  }
}

7. 调试与扩展

(1) 调试技巧
  • GDB 断点
     
    break opt_explain.cc:explain_query  # 跟踪 EXPLAIN 入口
    break sql_select.cc:Sql_cmd_dml::execute_inner  # 跟踪优化入口
  • 日志输出
     
    // 在 Query_expression::optimize 中打印优化步骤
    DBUG_PRINT("info", ("Optimizing query block %d", query_block->select_number));
(2) 扩展性
  • 自定义格式:通过继承 Explain_format 类实现新的输出格式。
  • 插件优化器:通过 Optimizer 插件接口扩展优化规则。

总结

MySQL 的 EXPLAIN 实现通过 多层模块协作 完成,从命令解析到优化器决策,最终生成用户可读的执行计划。其源码设计兼顾灵活性与性能,支持多种输出格式和复杂查询场景。理解这一机制,有助于深入掌握 MySQL 的查询优化逻辑,并为性能调优提供底层支持。

##gdb调试堆栈

(gdb) bt
#0  explain_query (explain_thd=0x7c292c001070, query_thd=0x7c292c001070, unit=0x7c292cd4cf70) at /home/yym/mysql8/mysql-8.1.0/sql/opt_explain.cc:2242
#1  0x000062de06742cd8 in Sql_cmd_dml::execute_inner (this=0x7c292cd0c590, thd=0x7c292c001070) at /home/yym/mysql8/mysql-8.1.0/sql/sql_select.cc:1020
#2  0x000062de06742067 in Sql_cmd_dml::execute (this=0x7c292cd0c590, thd=0x7c292c001070) at /home/yym/mysql8/mysql-8.1.0/sql/sql_select.cc:793
#3  0x000062de066b4841 in mysql_execute_command (thd=0x7c292c001070, first_level=true) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:4797
#4  0x000062de066b6cb3 in dispatch_sql_command (thd=0x7c292c001070, parser_state=0x7c2a111fd9f0) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:5447
#5  0x000062de066ac0d7 in dispatch_command (thd=0x7c292c001070, com_data=0x7c2a111fe340, command=COM_QUERY) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:2112
#6  0x000062de066a9f77 in do_command (thd=0x7c292c001070) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:1459
#7  0x000062de06901835 in handle_connection (arg=0x62de159f9b90) at /home/yym/mysql8/mysql-8.1.0/sql/conn_handler/connection_handler_per_thread.cc:303
#8  0x000062de08840bdc in pfs_spawn_thread (arg=0x62de159f8fd0) at /home/yym/mysql8/mysql-8.1.0/storage/perfschema/pfs.cc:3043
#9  0x00007c2a1fc94ac3 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#10 0x00007c2a1fd26850 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
(gdb) b Query_expression::optimize
Breakpoint 2 at 0x62de067fe9ac: file /home/yym/mysql8/mysql-8.1.0/sql/sql_union.cc, line 983.

##普通select 查询gdb堆栈

 

(gdb) bt
#0  Query_expression::execute (this=0x7c291070bad0, thd=0x7c2910000bf0) at /home/yym/mysql8/mysql-8.1.0/sql/sql_union.cc:1808
#1  0x000062de06742cf6 in Sql_cmd_dml::execute_inner (this=0x7c29105f3798, thd=0x7c2910000bf0) at /home/yym/mysql8/mysql-8.1.0/sql/sql_select.cc:1022
#2  0x000062de06742067 in Sql_cmd_dml::execute (this=0x7c29105f3798, thd=0x7c2910000bf0) at /home/yym/mysql8/mysql-8.1.0/sql/sql_select.cc:793
#3  0x000062de066b4841 in mysql_execute_command (thd=0x7c2910000bf0, first_level=true) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:4797
#4  0x000062de066b6cb3 in dispatch_sql_command (thd=0x7c2910000bf0, parser_state=0x7c2a0befb9f0) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:5447
#5  0x000062de066ac0d7 in dispatch_command (thd=0x7c2910000bf0, com_data=0x7c2a0befc340, command=COM_QUERY) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:2112
#6  0x000062de066a9f77 in do_command (thd=0x7c2910000bf0) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:1459
#7  0x000062de06901835 in handle_connection (arg=0x62de1335a2f0) at /home/yym/mysql8/mysql-8.1.0/sql/conn_handler/connection_handler_per_thread.cc:303
#8  0x000062de08840bdc in pfs_spawn_thread (arg=0x62de158573a0) at /home/yym/mysql8/mysql-8.1.0/storage/perfschema/pfs.cc:3043
#9  0x00007c2a1fc94ac3 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#10 0x00007c2a1fd26850 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
(gdb) p thd->query().str
$16 = 0x7c291070b9f0 "SELECT COUNT(*) FROM yym WHERE DATE_SUB(CURDATE(), INTERVAL 1 DAY)<=create_time\n LIMIT 0, 1000"


http://www.kler.cn/a/570440.html

相关文章:

  • 掌握 ElasticSearch 聚合查询:Aggregations 入门与实战
  • Vue.js 的双向数据绑定是如何实现的?
  • 快速创建基于Scala的flink开发项目
  • React 之 Redux 第二十八节 学习目标与规划大纲及概要讲述
  • chrome下载文件提示“贵组织屏蔽了该文件,因为它不符合安全政策” 安装chrome插件出现问题
  • 安装 cnpm 出现 Unsupported URL Type “npm:“: npm:string-width@^4.2.0
  • Hyper-V -docker-vmware 三者的关系
  • 零信任沙箱:为网络安全筑牢“隔离墙”
  • 6.人工智能与机器学习
  • 快瞳通用文档解析技术是怎样赋能下游各类大语言模型任务?
  • Lua | 每日一练 (4)
  • mapbox基础,使用geojson加载heatmap热力图层
  • 利用Java爬虫获取1688店铺所有商品信息:实战指南
  • 3. 前后端实现压缩包文件下载
  • 汽车电子电控软件开发中因复杂度提升导致的架构恶化问题
  • EVOAGENT: Towards Automatic Multi-Agent Generation via Evolutionary Algorithms
  • 【Linux】信号保存
  • JavaWeb后端基础(5)
  • go语言转换json字符串为json数据
  • Spring统一格式返回