MySQL并行查询仅支持全表扫描(type=ALL)或分区跨分区扫描,不支持索引范围扫描(type=range/ref),因优化器成本模型未纳入并发调度开销且B+树访问存在latch争用。

MySQL 对索引扫描的并行处理能力有限,根本原因不是“不支持并行”,而是优化器在绝大多数索引扫描场景下,主动选择不并行——哪怕你开了 innodb_parallel_read_threads 或升级到了 8.0+。
为什么EXPLAIN显示type=range却没走并行?
这是最常被误解的一点:EXPLAIN 中看到 type=range 或 ref,只说明走了索引,不代表能并行。MySQL 的并行查询(Parallel Query)目前仅对特定类型的 SELECT 生效,且有严格前提:
- 必须是全表扫描(
type=ALL)或分区表的跨分区扫描;索引范围扫描(哪怕扫几百万行)默认不触发并行 - 查询需满足“可拆分性”:不能含
ORDER BY、LIMIT、DISTINCT、用户变量、子查询等阻断并行的结构 -
innodb_parallel_read_threads仅控制读取线程数,但前提是优化器已决定启用并行——而它极少为索引扫描做这个决定
为什么优化器拒绝并行索引扫描?
核心在于成本模型和实现限制:
- 索引扫描天然具备局部性:B+树遍历是顺序+跳跃访问,多线程争抢同一棵索引树的页节点,容易引发
latch contention(尤其是dict_operation_lock和buf_pool_mutex),反而比单线程慢 - 优化器估算时,把索引扫描的成本建模为“逻辑页访问数 × 固定开销”,未引入并发调度代价项,导致并行路径的 cost 总是高于串行路径
- InnoDB 层缺乏细粒度的索引页级并行调度器;当前并行机制主要面向
TABLE SCAN场景,复用的是parallel_read基础设施,而非专为 B+ 树设计
哪些操作看似“走索引”实则触发了并行?
真正能触发并行的,往往是表面走索引、底层被迫退化为扫描的操作:
-
SELECT * FROM t WHERE json_col->'$.key' = 'val':如果json_col有虚拟列索引,但优化器判定无法精确下推,可能 fallback 到并行全表扫描 + JSON 过滤 - 分区表查询
WHERE partition_key IN (1,2,3):若每个分区都走独立索引扫描,MySQL 会为每个分区分配线程(伪并行),但这不是单个索引扫描的并行,而是分区级并发 -
SELECT COUNT(*) FROM t WHERE indexed_col > 1000:在某些 8.0.30+ 版本中,若统计信息表明结果集占比高(> ~30%),优化器可能放弃索引,改用并行TABLE SCAN
想让高并发下的索引查询更快,别指望并行
与其纠结并行,不如直面现实:
- 索引扫描瓶颈通常不在 CPU,而在 I/O(尤其是冷数据)或锁等待(
REPEATABLE READ下的 gap lock) - 真正有效的手段是:缩小扫描范围(加更精准的条件)、用覆盖索引避免回表、拆分大查询为多个小查询(如按时间分段)
- 如果真需要吞吐量,考虑应用层分片或读写分离,而不是依赖 MySQL 内部并行
记住:MySQL 的并行能力是“面向扫描,而非面向索引”的——这个设计取舍,在 8.0.x 系列里没有反转迹象。


















