mysql 8.0 并行扫描仅对无索引全表扫描的innodb大表查询生效,需显式配置innodb_parallel_read_threads>0且用explain format=tree验证,不适用于聚合、limit、索引查找等场景。

MySQL 8.0 并行扫描只对特定引擎和查询生效
并行扫描不是“所有 SELECT 都自动变快”,它只在 InnoDB 表上、配合 EXPLAIN FORMAT=TREE 能看到 Parallel scan 提示的场景才真正启用。最常见触发条件是:全表扫描(无有效索引)、WHERE 条件简单(如 WHERE id > 1000)、且查询涉及大量数据(通常 > 数百万行)。
MyISAM、Memory、分区表(即使底层是 InnoDB)都不支持;带 LIMIT、DISTINCT、GROUP BY、子查询或窗口函数的语句也大概率退回到单线程。
-
SELECT * FROM huge_table WHERE status = 1→ 可能并行(若 status 无索引) -
SELECT COUNT(*) FROM huge_table→ 不并行(聚合强制单线程) -
SELECT * FROM huge_table WHERE id IN (1,2,3)→ 不并行(走索引,不触发扫描)
必须手动开启并行度参数,且默认是关闭的
MySQL 8.0 默认关闭并行扫描,靠 innodb_parallel_read_threads 控制线程数,值为 0 表示禁用。它不是全局并发开关,而是每个满足条件的查询最多可使用的后台线程数(上限 4,设为 1 就等于没开)。
这个参数不能动态 SET GLOBAL 永久生效——它只在会话级临时有效,且仅对后续新执行的查询起作用;服务重启后恢复为 0。生产环境想长期使用,得写进配置文件 my.cnf 的 [mysqld] 段里。
- 临时启用:
SET SESSION innodb_parallel_read_threads = 4; - 配置文件写法:
innodb_parallel_read_threads = 4 - 值设太高反而可能拖慢:超过 CPU 核心数或 I/O 带宽时,线程争抢导致延迟上升
EXPLAIN 看不到并行?先确认 FORMAT 和执行计划层级
普通 EXPLAIN 输出里完全不会提“parallel”,必须用 EXPLAIN FORMAT=TREE 才能看到是否触发。而且只有当扫描成为执行计划最外层节点(比如没被嵌套在派生表或物化临时表里)时,才会显示 Parallel scan。
常见误判点:你加了 FORMAT=TREE,但输出第一行是 -> Limit 或 -> Materialize,那就说明并行根本没进到扫描层——优化器早就把它“优化掉”了。
- 正确观察方式:
EXPLAIN FORMAT=TREE SELECT * FROM t1 WHERE c1 > 100; - 看到类似
-> Parallel scan on t1 (cost=...)才算生效 - 如果输出里有
-> Filter: (t1.c1 > 100)包裹在扫描外,说明过滤逻辑在扫描后做,仍可能并行;但若Filter出现在扫描内部,大概率已降级
别指望小表提速,I/O 和锁竞争反而更明显
并行扫描本质是把大块数据切片、多线程读取,对 SSD 友好,但对 HDD 或高负载实例可能适得其反。小于 100MB 的表几乎看不出收益,反而因线程调度、内存分配、Buffer Pool 争抢引入额外开销。
更隐蔽的问题是:并行扫描期间,InnoDB 会以更粗粒度加 LOCK_S(共享锁),在高并发更新场景下,容易和 UPDATE/DELETE 冲突,表现为 Lock wait timeout exceeded 或查询卡住几秒。
- 建议压测对比:开 / 关
innodb_parallel_read_threads下的 P95 延迟和 QPS - 监控指标重点看:
Innodb_buffer_pool_wait_free、Threads_created、磁盘 await - 线上灰度时,优先在只读从库试,避免主库锁竞争放大
实际用起来,最常被忽略的是「并行不等于并发」——一个查询用 4 个线程扫一张表,和 4 个查询各自单线程扫,资源消耗模式完全不同。调参前先看 I/O 饱和度和锁等待,比盲目开 4 更管用。











