执行器不直接调用存储引擎,而是通过handler接口层中转;调用的ha_index_read()、ha_rnd_next()等是server层虚函数入口,实际执行的是innodb等引擎注册的子类实现,如ha_innobase::index_read()。

执行器不直接调用存储引擎,而是通过 handler 接口层中转——这是最常被误解的一点。你看到的 ha_index_read()、ha_rnd_next() 等函数,本质是 Server 层定义的虚函数入口,真正干活的是 InnoDB 或 MyISAM 等引擎注册的具体实现。
为什么断点打在 handler::index_read() 却没命中
因为执行器调用的是基类声明的接口,实际跳转到的是子类(如 ha_innobase::index_read())。调试时若只在基类函数下断点,必然不触发。
- 确认是否真进了 InnoDB:在 GDB 中用
info symbol $pc查当前函数名,或直接对ha_innobase::index_read下断点 -
ha_rnd_next()返回HA_ERR_END_OF_FILE但表明明有数据?先检查ha_rnd_init()是否成功返回 0;再确认事务是否已开启(InnoDB 要求有效prebuilt->trx) - MyISAM 场景下该函数可能读到未提交脏数据——它不走 MVCC,只依赖
LOCK TABLES粗粒度控制
ha_index_read() 的 find_flag 参数怎么影响行为
这个参数决定索引扫描语义,不是“查到了就停”,而是告诉引擎你要什么逻辑:
-
HA_READ_KEY_EXACT:等值匹配,如WHERE a = 1 -
HA_READ_KEY_OR_NEXT:找到第一个 ≥ 目标键的位置,用于WHERE a >= 1 -
HA_READ_AFTER_KEY:跳过等于目标键的记录,从下一个开始,常见于ORDER BY ... LIMIT分页优化 - 误用
HA_READ_KEY_EXACT去扫范围条件,会导致只读一条就返回HA_ERR_KEY_NOT_FOUND
EXPLAIN 显示 “Using index” 却没进 ha_index_read() 怎么回事
说明走了覆盖索引优化路径——执行器压根没让引擎构造完整行记录,而是直接从 B+ 树叶子节点解析字段值。
- 典型场景:
CREATE INDEX idx_ab ON t(a,b); SELECT a,b FROM t WHERE a=1;→ 所有字段都在索引里,Server 层调key_copy()拷贝,绕过引擎层解包逻辑 - InnoDB 支持该优化;MyISAM 不支持,哪怕同样满足覆盖条件也会走
ha_index_read() - 只要
SELECT中出现非索引字段(比如c是TEXT且不在索引中),就必须回聚簇索引,触发ha_index_read() - 想强制走引擎路径?加
FORCE INDEX(idx_ab)并选一个非覆盖字段,比如SELECT a,b,c FROM t FORCE INDEX(idx_ab) WHERE a=1;
external_lock() 被反复调用却不报错,正常吗
完全正常。它不是锁操作本身,只是通知引擎“当前线程要访问这张表了”。
- 每条 SQL 开始前调一次
external_lock(LOCK_TABLE_READ)或LOCK_TABLE_WRITE - SQL 结束后调一次
external_lock(UNLOCK_TABLE) - InnoDB 用它绑定
prebuilt->trx和锁等待队列;MyISAM 则基本忽略 - 别把它当成锁失败信号——即使频繁调用,只要没卡在
wait_lock或返回错误码,就是健康状态
真正容易被忽略的是事务上下文有效性:InnoDB 的 handler 函数第一件事往往是检查 prebuilt->trx 是否有效,而这个对象依赖于连接是否处于活跃事务中。很多“查不到数据”的问题,根源不在索引或 SQL 写法,而在事务没显式开启或已意外回滚。











