人人都会AI编程

执行器:调用存储引擎执行

更新时间:2026-07-11

当优化器生成了执行计划,SQL 语句的执行就进入了最后阶段——执行器开始工作。简单来说,执行器是执行计划的忠实执行者,它按照优化器给出的步骤,一步步调用存储引擎的接口来完成数据的读取和写入。

执行器的核心职责

执行器本身不直接接触磁盘上的数据文件,它在服务层与存储引擎层之间扮演着“指挥者”的角色:

  1. 权限检查:在执行之前,执行器会根据执行计划中涉及的表,检查当前连接的用户是否具备相应的权限(SELECT、INSERT、UPDATE、DELETE 等)。如果权限不足,直接返回错误,不会调用存储引擎。这一点很重要:权限检查在表打开后进行,而不是在解析阶段。所以,即使解析阶段看不出问题,执行时也可能因为权限不足而报错。
  1. 调用引擎接口:以最常见的 SELECT 语句为例,执行器的典型流程是:
  • 调用引擎的 index_initrnd_init 初始化扫描。
  • 循环调用 index_readrnd_next 逐行获取数据(具体取决于执行计划用的是索引扫描还是全表扫描)。
  • 对于有 WHERE 条件的,执行器会评估条件表达式,只保留满足条件的行。对于涉及多个表的 JOIN,执行器按照优化器选定的嵌套循环顺序,逐表取出行并匹配。
  • 如果有排序(ORDER BY)、分组(GROUP BY)、聚合函数等操作,执行器会根据计划判断是否已经可以利用索引有序返回而省略排序,否则会在临时表或排序缓冲区中完成这些操作。
  • 最终将结果集通过连接层返回给客户端。
  1. 更新操作的处理:对于 INSERT、UPDATE、DELETE,执行器也是逐行调用引擎的写入接口。特别地,在执行 UPDATE 时,执行器会先调用引擎接口读取要更新的行,然后在服务层计算出新的列值,再调用引擎的 update_row 接口将新值写回。这当中涉及 undo log 的生成(用来实现事务回滚和 MVCC)、redo log 的写入(保证持久性),以及可能触发的索引更新等。这些对执行器来说都是透明的,由存储引擎内部协调完成。

一个直观的例子

假设有一条 SQL:

SELECT name FROM users WHERE age > 18;

执行器接收到优化器生成的执行计划,比如计划是“使用 age 索引扫描”。那么执行器的操作大致为:

  • 检查 users 表权限 → 通过。
  • 调用 InnoDB 引擎的索引扫描接口,从 age 索引中定位到第一条 age>18 的记录。
  • 通过索引记录中的主键值,去主键索引(聚簇索引)中回表取出完整行数据。
  • 从行数据中提取 name 字段,放入结果集。
  • 继续沿着 age 索引扫描下一条满足条件的记录,重复上述过程,直到索引扫描完毕。
  • 向客户端返回结果集。

在这个过程中,执行器并不知道 age 索引是 B+ 树还是哈希结构,也不知道回表的具体细节——它只负责调用引擎提供的接口。这种分层设计正是 MySQL 插件式存储引擎架构的精髓。

与存储引擎的交互细节

执行器和存储引擎之间的交互遵循一套固定的 API 模式,这套 API 定义在源码的 handler 类中。引擎需要实现诸如 opencloseindex_readrnd_nextwrite_row 等方法。这种标准化的接口让 MySQL 可以无缝切换或混用不同的存储引擎。

一个实际开发中可能需要留意的点是:执行器返回记录时是流式的。并不是等所有行都读取完再一次性发送给客户端,引擎每返回一行,执行器就可以评估条件,一旦满足就立即发送(如果客户端也支持)。这带来了内存占用小的好处,但同时也意味着如果查询中途遇到错误(例如由于引擎层锁超时),客户端可能已经接收到了部分结果行。这种半完成的结果在事务中可能被回滚,但已经发给客户端的行无法撤回,需要应用层注意处理。

执行器的实际影响

虽然大部分时候执行器只是“照章办事”,但它的一些行为会对性能产生直接影响:

  • 条件评估的前置与后置:优化器可能选择将一些 WHERE 条件作为索引条件(Index Condition Pushdown,ICP)下推到引擎层,这样引擎在索引层面就能过滤掉更多行,减少回表次数。当执行计划中出现 Using index condition 时,就表示执行器将这部分条件留给了引擎在扫描索引时处理。
  • 全表扫描的处理:如果执行计划决定全表扫描(type=ALL),执行器会调用引擎的 rnd_next 接口,一行一行地从聚簇索引中顺序读取所有行。对于大表,这非常耗时,且会持续向缓冲池中加载数据页,可能导致热数据被挤出,需要特别避免。
  • 查询缓存(8.0 已移除):在 MySQL 5.7 版本中,执行器执行查询前还会检查查询缓存,如果命中则直接返回结果,连引擎都不用调。但这个功能在高并发下锁竞争严重,8.0 已彻底移除。

理解执行器的工作原理,有助于你解读 EXPLAIN 的输出、理解为什么一条查询慢,以及判断索引是否被有效利用。当你在 EXPLAIN 中看到 Using index conditionUsing whereUsing temporaryUsing filesort 等信息时,这些实际上都是在告诉你执行器在这一步做了什么额外的处理。优化 SQL 时,很多优化目标就是让执行器能够以更高效、更少额外操作的方式调用存储引擎。