chunk-size过小会导致事务开销剧增、binlog写放大、从库延迟升高,并使--throttle误判负载而频繁休眠,引发执行停滞;建议从1000起步实测调优。

chunk-size 设定过小会导致什么
pt-online-schema-change 每次只改一小块数据,靠 chunk-size 控制单次处理行数。设得太小(比如 100),会显著放大事务开销和锁等待:
- 每个 chunk 都要启一个新事务、查主键范围、做 INSERT/UPDATE、再校验,频繁提交让 binlog 写放大、从库延迟跳升
- 更致命的是,小 chunk 会让
--throttle判定更敏感——哪怕只是临时 IO 尖峰,也容易被误判为“过载”,主动 sleep,整体耗时翻倍甚至停滞
常见现象:执行日志里反复出现 Throttling due to load,但 SHOW PROCESSLIST 看不到明显阻塞,CPU 和磁盘 IO 实际利用率却很低。
建议从 1000 起步,在低峰期实测:
-
--chunk-size=1000+--throttle=50(单位 ms)先跑 5 分钟,观察Threads_running和Innodb_row_lock_time_avg - 若锁等待时间稳定 5000;若 > 5ms,说明表热点集中,得降回
1000并考虑拆分 WHERE 条件
throttle 值不是越小越安全
--throttle 的作用是“每处理完一个 chunk 后,检查系统负载,超阈值就睡一会”。它不控制单次操作快慢,只管“喘气节奏”。
设成 1 或 5 看似保守,实际反而危险:
- 太频繁的负载探测(每毫秒一次)本身就会增加性能抖动,尤其在高并发写入场景下,可能触发 MySQL 内部状态采样竞争
- 它只看
Threads_running和Threads_connected,对磁盘 IO、内存压力、复制延迟完全无感——你卡在 IO 上,它还傻等连接数下来
真正该盯的是:
-
SHOW GLOBAL STATUS LIKE 'Threads_running'长期 > 30?说明并发已饱和,--throttle可设100强制降速 - 主从延迟 > 30s?优先关掉
--throttle,用--max-lag控制复制 lag,否则 throttle 睡眠根本救不了从库
chunk-size 和 throttle 必须配合 where 条件用
pt-online-schema-change 默认按主键范围切 chunk,但如果加了 --where,行为会变:
-
--where "status='pending'"这种非主键条件,会导致每个 chunk 都要全表扫描过滤,chunk-size=1000可能实际扫 5 万行才凑够 1000 条 - 此时
--throttle的休眠反而掩盖了真实瓶颈——你以为是负载高,其实是索引没走对
验证方法很简单:
- 加
--dry-run和--print,看生成的 SELECT 语句是否命中索引(用EXPLAIN手动跑一遍) - 如果
type是ALL或index,必须给--where字段建索引,否则chunk-size再大也没用 - 索引存在但没走?可能是统计信息过期,
ANALYZE TABLE一下再试
别信默认值,生产环境必须压测验证
pt-online-schema-change 的默认 chunk-size=1000 和无 --throttle,只适合测试库。线上表行数超千万、QPS > 500 时,这两值基本无效。
压测关键动作:
- 用
--dry-run --execute --print先导出真实 SQL,拿慢查询日志里的典型 UPDATE/INSERT 拆出来,在从库上用sysbench或mysqlslap模拟并发压 - 观察
Innodb_buffer_pool_wait_free是否上涨——涨了说明 buffer pool 不够,chunk-size 得往小调 - 开启
--progress=time,5,对比不同配置下每 5 秒完成的 chunk 数,波动超过 ±30% 就说明当前组合不稳定
最常被忽略的一点:--chunk-index 默认用主键,但如果主键是 UUID 或写入热点集中在某一段(比如时间戳倒序),chunk 切片会严重不均——前几个 chunk 很快,后几个卡死。这时候得手动指定一个分布均匀的索引,哪怕多建一个 created_at 单列索引也比硬扛强。










