mysql 8.0 对 in 常量列表底层优化是默认用哈希查找替代排序+二分,前提是纯常量且数量适中(≤1000);提速源于省去排序开销和 o(1) 内存查找,但 explain 不体现该机制,需通过 format=tree 或性能对比验证。

MySQL 8.0 对 IN 常量列表做了哪些底层优化?
MySQL 8.0 没有给 IN 加新语法,但内部执行路径确实变了:它默认用哈希查找替代旧版的排序+二分,前提是常量列表能被优化器识别为“可哈希集合”。这不是配置开关,而是解析阶段自动触发的行为——只要 IN 后面是纯常量(如 IN (1, 2, 3)),且数量不太离谱(一般 ≤ 1000),就会走这个路径。
为什么 EXPLAIN 看不出“哈希查找”,但实际更快了?
EXPLAIN 不会显示“用了哈希表”,它只反映索引访问方式(比如 type=range 或 type=ref)。真正提速来自两处:
- 不再对常量列表做排序(旧版 MySQL 5.7 会排),省掉 O(n log n) 开销
- 匹配时直接查内存哈希表,O(1) 查找,而非在有序列表里二分(O(log n))
- 如果左侧字段有索引,优化器仍会生成多个等值范围(
id=1 OR id=2 OR id=3),但这些范围会被 B+ 树高效合并扫描,不是逐个跳转
什么情况下这些优化会失效?
一旦打破“纯常量”前提,优化就退回到传统路径:
- 混入变量或函数:如
IN (1, @var, UNIX_TIMESTAMP())→ 哈希失效,可能转成 OR + 全表扫描 - 字符串长度差异大:比如
IN ('a', 'bb', 'ccc', ..., 'x' * 255),哈希建表开销变高,优化器可能放弃 - 列表过大(如 > 5000 个 int):触发
eq_range_index_dive_limit机制,默认 200,超过后优化器改用统计估算,容易选错执行计划 - 字段无索引:哈希查找只加速“判断是否在列表中”,但没索引时仍要全表扫描每行,哈希本身不减少 IO
怎么验证当前查询是否受益于新优化?
别只看 EXPLAIN,重点对比实际执行行为:
- 执行
SELECT * FROM t WHERE c IN (常量列表),再执行EXPLAIN FORMAT=TREE,观察是否有rows_examined_per_scan显著低于列表长度(说明没逐个匹配) - 用
SHOW PROFILE或 Performance Schema 查Handler_read_next次数:优化后该值应接近匹配行数,而非接近 IN 列表长度 - 把同一查询在 MySQL 5.7 和 8.0 上跑,相同数据量下,8.0 的 CPU 时间通常低 20%~40%,尤其当列表含 100–500 个值时
真正容易被忽略的是:哈希优化只作用于常量列表本身,它不解决索引缺失、参数膨胀或网络传输瓶颈。如果你传了 2000 个 ID,哪怕 8.0 内部哈希再快,SQL 文本体积和 max_allowed_packet 风险依然存在。











