案例 innodb_parallel_read_threads 实测:count(*) 18.2s→2.3s、在线 DDL 50%→18min、CHECK TABLE 45min→5min

2026-09-09 21:34:53

innodb_parallel_read_threads 实测:count(*) 18.2s→2.3s、在线 DDL 卡 50%→18min、CHECK TABLE 45min→5min

MySQL 8.4 LTS 中 innodb_parallel_read_threads 的默认值已改为自适应,但默认值并不总是匹配业务负载和硬件。这篇文章记录三次调优:电商用户表 count(*)、日志表在线 DDL、政务表 CHECK TABLE

机制上这个参数做的是任务分片:单线程扫聚簇索引时,只有一个读线程在顺序读数据页;调大线程数后,InnoDB 把聚簇索引扫描范围拆成多个分片,由多个 worker 并发读取各自分片。三个案例环境差异较大,结论不能直接照搬,所以每步都先在会话级验证,再固化为配置。

案例表规模环境MySQL 版本操作调整前 → 调整后
电商user_info 4200 万+行 / 18GB32 核、64GB、SSD8.4.5SELECT COUNT(*)18.2s → 2.3s
日志分析app_log 1.8 亿行 / 200GB64 核、128GB、NVMe SSD8.4.5ALTER TABLE ADD INDEX60min 卡在 50% → 18min
政务business_data 约 8000 万行 / 80GB16 核、32GB、SAS HDD8.4.2CHECK TABLE45min → 5min

案例 1:电商 user_info,count(*) 从 18.2s 到 2.3s

报表系统每天凌晨 3 点执行:

SELECT COUNT(*) FROM user_info;

user_info 约 4200 万行,数据量约 18GB。每次执行耗时 18~20 秒,执行期间持续产生 I/O 压力,订单同步任务偶发超时告警。

从慢日志看,rows_examined 为 4200 多万,Extra 显示 Using index,说明语句没走全表扫描,扫的是主键聚簇索引。但 top 显示 CPU 利用率只有 3%~5%,32 核机器上基本只有一个线程在忙。

不直接改全局配置,先在会话级测试:

SET innodb_parallel_read_threads = 8;
SELECT COUNT(*) FROM user_info;   -- 8.7s

SET innodb_parallel_read_threads = 16;
SELECT COUNT(*) FROM user_info;   -- 2.3s

SET innodb_parallel_read_threads = 32;
SELECT COUNT(*) FROM user_info;   -- 2.5s,反而变慢

线程数不是越大越好。从 16 加到 32 之后,线程切换开销开始抵消并行收益。确定 16 为当前实例的最优值后,写入 my.cnf,并执行 SET PERSIST innodb_parallel_read_threads = 16; 使其立即生效,不需要重启。

实际效果:查询从 18.2s 降到 2.3s,CPU 利用率升到 25%~30%,订单同步不再超时。调参时不要追求 CPU 打满,需要为业务线程留出调度余量。

案例 2:app_log 在线 DDL 卡在 50%,调线程后 18 分钟完成

app_log 表 1.8 亿行,约 200GB,运行在 64 核、128GB 内存、NVMe SSD 实例上,MySQL 8.4.5。日志表 7x24 写入,因此加索引必须在线执行:

ALTER TABLE app_log
  ADD INDEX idx_create_time (create_time),
  ALGORITHM=INPLACE, LOCK=NONE;

第一次执行跑了 60 分钟,进度只到 50%,之后基本停滞。观察系统状态:I/O 利用率 95%,CPU 利用率只有 8%。瓶颈在单线程扫描聚簇索引,不在后续的排序和构建阶段。

在线 DDL 创建二级索引大致分两段:

  • 扫描聚簇索引,读取全表数据用于构建索引,这一段由 innodb_parallel_read_threads 控制;
  • 对索引键排序、构建二级索引,这一段由 innodb_ddl_threads 控制,默认 4 个线程通常够用。

超大表加索引,瓶颈多数在扫描阶段。这次在会话级把扫描线程调到 32(64 核的一半):

SET innodb_parallel_read_threads = 32;

ALTER TABLE app_log
  ADD INDEX idx_create_time (create_time),
  ALGORITHM=INPLACE, LOCK=NONE;

执行期间 CPU 利用率升到 40%,I/O 稳定在 70%,进度匀速推进。整个 DDL 耗时 18 分钟,其中扫描阶段 8 分钟,业务无感知。

注意这个参数的边界:它只控制聚簇索引扫描阶段,不控制后续索引排序与构建。如果调大后耗时仍长,且瓶颈在排序或构建阶段,需要查 innodb_ddl_threads

案例 3:政务 CHECK TABLE,45 分钟缩到 5 分钟

政务场景一张约 8000 万行、80GB 的民生业务表 business_data,部署在 16 核、32GB 内存、SAS 机械盘实例上,MySQL 8.4.2。

MySQL 8.4 的自适应默认值在这台机器上算出来是 16/8=2,低于该参数最小值 4,所以实际默认值是 4。每周日凌晨执行 CHECK TABLE business_data 需要 45 分钟,执行期间持有共享锁,阻塞了后续计划内的索引优化任务。

CHECK TABLE 第二阶段的索引完整性校验支持并行扫描,同样受 innodb_parallel_read_threads 控制。在维护窗口内做了三组会话级测试:

SET innodb_parallel_read_threads = 4;
CHECK TABLE business_data;   -- 45min(基准)

SET innodb_parallel_read_threads = 8;
CHECK TABLE business_data;   -- 12min

SET innodb_parallel_read_threads = 12;
CHECK TABLE business_data;   -- 5min

一个与直觉相反的观察:SAS 机械盘场景下并行效果比 SSD 更明显。机械盘单线程读时延迟高,串行等待时间都暴露在关键路径上;多线程并发读取可以让多个请求同时处于等待状态,用并发掩盖延迟,提升整体 I/O 利用率。

最终配置:全局参数设为 8,兼顾日常 count(*) 和常规查询;每周日 CHECK TABLE 维护脚本中先 SET SESSION innodb_parallel_read_threads = 12; 再执行检查。

使用边界与调参顺序

这个参数只对三类场景有效:

  1. 不带 WHEREcount(*) 查询;
  2. CHECK TABLE 第二阶段索引校验;
  3. 在线 DDL 创建二级索引时的聚簇索引扫描阶段。

WHERE 的查询、JOIN 关联查询,调这个参数无效。

不同表规模的参考线程数:

表规模建议线程数
百万级以下保持默认
千万级逻辑 CPU 核数一半,上限 16
亿级逻辑 CPU 核数一半到三分之二,上限 64

避坑三点:

  • 高峰期不要直接调大全局值,并行扫描会占用额外 CPU 和 I/O;
  • 表存在虚拟列、全文索引或空间索引时,并行扫描会自动退化为单线程,调参无效不是参数问题,先检查索引类型;
  • 并行扫描读入的数据页放在 LRU 列表尾部,用后很快淘汰,不必担心缓冲池污染。

最终判断:不要固定一个全局万能值。同一个参数在不同核数、不同存储介质、不同表大小上的最优值都不一样,先按 4/8/16/32 做会话级测量,观察耗时、CPU、I/O 的趋势,出现收益递减就停,再固化成 my.cnfSET PERSIST 或定时任务里的会话级值。

复制全文 生成海报 MySQL InnoDB 并行扫描 运维调优

推荐文章

程序员茄子在线接单