explain显示走索引但查询仍慢,是因为自适应哈希索引(ahi)未生效——它仅对满足高频、纯等值、页驻留缓冲池等条件的热点索引页按需构建哈希映射,不支持like、范围查询、联合索引前缀不匹配等场景;且高并发下btr_search_latch争用反而拖慢qps。

为什么EXPLAIN显示走索引,但查询还是慢?
因为AHI根本没生效——它不是“只要建了索引就自动加速”,而是只对满足特定条件的等值查询、且被InnoDB判定为“热点”的索引页才构建哈希条目。常见现象是:EXPLAIN显示type=ref或const,SHOW ENGINE INNODB STATUS\G里却看到adaptive hash searches: 0、Hash table size长期卡在个位数。
原因包括:
- 查询条件不是纯等值:比如
WHERE name LIKE 'abc%'、WHERE age > 25、ORDER BY id,全部绕过AHI - 联合索引前缀不匹配:索引是
(a,b,c),却查WHERE b = 2 AND c = 3,无法生成有效hash info - 数据太分散:单个索引页访问频次不够内部阈值(约每10次索引查找中触发1次),InnoDB不认为它是“热点”
- Buffer Pool刚启动或过小:
Hash table size初始极小,且没有足够内存容纳哈希桶和链表节点
什么时候AHI反而拖慢QPS?
AHI查得快,但所有读写操作(包括SELECT、UPDATE、INSERT)都要短暂持有btr_search_latch才能决定是否走哈希路径。这个latch在MySQL 5.7+被分成innodb_adaptive_hash_index_parts个分区,默认8个。
高并发主键点查场景下容易出问题:
-
SHOW ENGINE INNODB STATUS反复出现waiting for btr_search_latch -
innodb_row_lock_time_avg飙升,但Innodb_row_lock_waits几乎不变——说明不是行锁争用,是latch串行化了 - buffer pool命中率已很高(>99%)时,AHI带来的收益远小于latch开销
实测关闭innodb_adaptive_hash_index后,QPS可能提升20%~40%。
怎么判断当前该开还是该关?
别凭感觉,看三项实时指标:
- 查
SHOW ENGINE INNODB STATUS\G,重点关注Hash table size(真实桶数)和node heap(链表节点数):若长期size 且<code>node heap = 0,说明AHI基本没激活 - 对比
adaptive hash searches与select full range join或select range的比值:若前者占比 - 观察
innodb_buffer_pool_read_requests与innodb_buffer_pool_reads:buffer pool命中率>99.5%时,AHI加速意义已大幅下降
关闭AHI后要注意什么?
关闭本身很简单:SET GLOBAL innodb_adaptive_hash_index = OFF;,或配置文件加innodb_adaptive_hash_index = OFF并重启。但要注意:
- 这不是一劳永逸的开关:业务模式变化后(比如突然出现某ID高频点查),AHI可能重新变得有价值
- 关闭后
Hash table size会清零,但不会立即释放内存;重启MySQL才能彻底回收AHI占用的buffer pool空间 - 某些OLAP类查询(如宽表上固定
WHERE dim_id = X)可能受益于AHI,关掉后需确认这类SQL的延迟变化
AHI真正的复杂点在于它的“自适应”是隐式的、不可控的——你没法指定给哪张表、哪个索引、哪个值建哈希,只能靠监控反推它有没有在干活。最容易被忽略的是:它既不是索引优化器的一部分,也不参与执行计划生成,而是在B+树查找路径上“偷偷插队”的一层缓存逻辑。











