
MySQL 什么时候会自动创建 AHI(自适应哈希索引)
MySQL 的自适应哈希索引(AHI)不是你 CREATE 的,也不是配置开关一开就全局生效的——它是 InnoDB 在运行时「偷偷」为某些热点 b+tree 页面构建的哈希索引,只对满足特定访问模式的查询生效。
关键条件有三个,缺一不可:
- InnoDB 正在使用
COMPACT或REDUNDANT行格式(DYNAMIC/COMPRESSED下 AHI 生效概率大幅降低) - 同一页面被连续以相同搜索条件(如相同
WHERE id = ?)随机查找 ≥ 17 次(这是硬编码阈值,由innodb_adaptive_hash_index_parts和内部计数器共同触发) - 该页面的 B+Tree 搜索路径稳定(比如主键等值查、二级索引等值查且回表少),不能是范围扫描或频繁更新的页
所以别指望 SELECT * FROM t WHERE created_at > '2024-01-01' 这种范围查询触发 AHI;它只认“反复查同一个值”的场景。
怎么确认 AHI 当前是否起作用
看 SHOW ENGINE INNODB STATUS 的 ROW OPERATIONS 小节,重点关注两行:
Hash table size 4425293, used cells 0, node heap has 0 buffer(s) Hash table size 4425293, used cells 12345, node heap has 6789 buffer(s)
used cells 大于 0 才说明有活跃 AHI 条目;但更关键的是下面这行:
0.00 hash searches/s, 0.00 non-hash searches/s
如果 hash searches/s 明显大于 0(比如 > 100),且和你的 QPS 匹配,才说明 AHI 真正在加速查询。注意:这个统计是全局累计值,重启后清零。
另外,INFORMATION_SCHEMA.INNODB_METRICS 中的 adaptive_hash_searches 和 adaptive_hash_searches_btree 可以对比看出 AHI 命中率(前者 / 后者 ≈ 实际加速比)。
AHI 导致锁竞争或性能抖动的典型场景
AHI 是全局共享结构,所有并发查询都要争抢它的 latch(主要是 hash_table->latch)。高并发等值查询下,容易出现:Waiting for table flush 或 waiting for adaptive hash latch —— 这不是锁表,是卡在 AHI 内部同步上。
常见诱因:
- 大量短连接反复执行相同
SELECT ... WHERE pk = ?(比如微服务里没用连接池的 HTTP 接口) -
innodb_adaptive_hash_index_parts设置过小(默认 8),导致单个 latch 保护太多 bucket,争抢加剧 - 表有高频更新(尤其是聚集索引页分裂),AHI 条目频繁失效重建,反而增加 CPU 开销
实操建议:若监控发现 adaptive_hash_searches_btree 激增但 adaptive_hash_searches 几乎为 0,大概率是 AHI 在“白忙活”,此时可考虑关掉:SET GLOBAL innodb_adaptive_hash_index = OFF(动态生效,无需重启)。
为什么改了 innodb_adaptive_hash_index 配置却没效果
因为 AHI 不是“开关即开即用”。即使设成 ON,它也只在满足前述访问模式时才逐步构建;反过来,设成 OFF 后,已有的 AHI 条目不会立刻清空,而是等对应 B+Tree 页面被驱逐出 buffer pool 时才释放。
还有两个易忽略点:
-
innodb_adaptive_hash_index是动态变量,但部分旧版本(如 MySQL 5.6.23 之前)不支持运行时关闭,必须重启 - Percona Server 或 MariaDB 可能有额外参数(如
innodb_adaptive_hash_index_partitions),和官方 MySQL 的innodb_adaptive_hash_index_parts行为不完全一致 - AHI 对
READ UNCOMMITTED和READ COMMITTED隔离级别更友好;在REPEATABLE READ下,若事务内多次查同一行,可能因 MVCC 版本判断绕过 AHI
真正要验证是否生效,别只看变量值,盯住 SHOW ENGINE INNODB STATUS 里的实时计数器,那是唯一可信信号。










