MySQL在运行时自动为频繁等值查询的B+树叶子页构建自适应哈希索引(AHI),仅加速单值=查询,不显式创建、不可配置开关直接开启,依赖热点页访问模式与buffer pool驻留时间。

MySQL 什么时候会自动启用自适应哈希索引(AHI)
自适应哈希索引不是你 CREATE 的,也不是配置开关能直接打开的——它是 InnoDB 在运行时「偷偷建」的,只对满足条件的热点 B+ 树索引页 自动构建哈希映射。触发前提是:同一索引页被连续、频繁地以相同模式访问(比如等值查询 WHERE id = ?),且该页在 buffer pool 中停留时间足够长。
常见错误现象:SHOW ENGINE INNODB STATUS 里看到 adaptive hash index 部分有非零 hash searches/s,但 queries to hash table 却很低——说明 AHI 建了,但没被有效命中,可能因为查询条件不匹配哈希键结构(比如用了范围查询或前导列缺失)。
- AHI 只加速单值等值查找(
=),对IN、BETWEEN、LIKE 'abc%'无效 - 只作用于 B+ 树叶子页(leaf page),非叶子页不参与
- 哈希键由索引字段值 + 索引页地址联合生成,所以不同页上的相同键值不会冲突,但也不会共享哈希项
- 如果 buffer pool 压力大,InnoDB 可能主动删掉部分 AHI 来腾出空间,不是永久存在
怎么确认某条查询是否走上了 AHI
不能靠 EXPLAIN,它完全不显示 AHI;也不能靠慢日志里的 rows_examined,因为 AHI 查找不算“行扫描”。唯一可靠方式是看 INNODB STATUS 的实时统计,或者用 performance_schema 观察哈希查找行为。
实操建议:
- 执行
SHOW ENGINE INNODB STATUS\G,滚动到INSERT BUFFER AND ADAPTIVE HASH INDEX小节,关注hash searches/s和non-hash searches/s的比值——比值越高,说明 AHI 越活跃 - 对比两次状态输出,手动跑几轮相同
SELECT ... WHERE pk = ?查询,看hash searches是否明显增长 - 禁用 AHI 测试(仅限调试):
SET GLOBAL innodb_adaptive_hash_index = OFF;,观察 QPS 或延迟变化;但别在线上长期关,尤其 OLTP 场景
AHI 对性能的实际影响和风险点
AHI 是把双刃剑:对简单主键/唯一键等值查询,能省掉 B+ 树从根到叶子的遍历(通常 2~4 次 page read),延迟下降明显;但它也吃内存、加锁开销,并可能在高并发下成为争用点。
容易踩的坑:
- AHI 内存不计入
innodb_buffer_pool_size,但实际占用 buffer pool 内存(每个哈希项约 100 字节),大量热点页可能导致 buffer pool 实际可用空间缩水 - AHI 构建和维护需要持有
btr_search_latch,高并发等值查询下这个 latch 可能成为瓶颈,表现为SHOW PROFILE中searching rows阶段耗时突增 - 如果表有多个二级索引,且访问模式分散,AHI 可能为多个索引页建哈希,但命中率低,纯属浪费资源
-
innodb_adaptive_hash_index_parts默认为 1,多核机器建议设为 CPU 核数(如 8),避免单 latch 争用;但设太高会增加管理开销
什么情况下该考虑关闭或调优 AHI
不是所有系统都适合开 AHI。典型适用场景是读多写少、主键查询密集、buffer pool 充足;而以下情况更应谨慎:
- 混合负载中写入压力大(如频繁
INSERT/UPDATE/DELETE),因为每次索引页变更都要同步更新对应 AHI,反而拖慢写路径 - 查询模式高度动态(比如参数化查询中
WHERE status IN (?, ?, ?)占比高),AHI 几乎不生效 - 使用了压缩表(
ROW_FORMAT=COMPRESSED)或页大小非默认(16K),AHI 兼容性弱,可能失效或引发断言错误 - 监控发现
innodb_mutex_spin_waits中btr_search_latch占比持续 > 10%,且hash searches/s增长缓慢,说明 AHI 在拖后腿
AHI 的复杂点在于它完全隐式运作,没有 per-table 或 per-index 控制粒度,也没法指定哪些查询走哈希。你只能通过 workload 特征、内存压力、latch 争用三者交叉判断——它到底是在帮你,还是在悄悄拖慢你。


















