对 MySQL 大表执行在线结构变更时,pt-online-schema-change 是最常用的工具之一。它通过在原表上建立触发器、创建新表并分批拷贝数据来实现无锁变更,但默认配置在真实生产环境往往过于激进。其中 chunk-size 与 throttle 两个参数直接决定了拷贝过程对数据库造成的压力,理解它们的运作方式并合理调优,是避免主库抖动和从库延迟爆雷的关键。

chunk-size 的工作原理与影响
chunk-size 参数决定了 pt-online-schema-change 每次从原表拷贝到新表的行数。工具内部会以主键或唯一索引为边界,将全表切分为多个数据块,逐块执行 INSERT INTO ... SELECT 语句。当 chunk-size 设置过大时,单个事务需要读取和写入的行数变多,会产生较大的临时表空间占用、更长的行锁持有时间,以及突发的 redo log 与 binlog 写入量。
在机械盘或混合读写负载的实例上,过大的 chunk-size 容易让 IO 在一次拷贝中被打满,进而影响线上正常查询。反之,chunk-size 过小会导致整体拷贝次数增多,触发器维持和表切换的额外开销被放大,总变更时间拉长。一般建议从较小的值开始压测,再逐步上调到延迟可接受的上限。
# 将每次拷贝行数限制为 500,降低单批事务压力 pt-online-schema-change --alter="ADD COLUMN remark varchar(255)" --chunk-size=500 --no-drop-old-table D=test,t=orders
throttle 限流机制解析
throttle 的作用是让工具在检测到系统压力达到阈值时主动暂停拷贝。它并不是简单固定休眠,而是支持多种判断条件,例如主库的 threads_running、从库的 Seconds_Behind_Master,或者自定义脚本的返回值。工具在每完成一个 chunk 后会检查节流条件,若命中则 sleep 指定时间后再继续。
很多人在配置时误以为 throttle 就是加一个固定 --sleep,这会让拷贝过程盲目降速,既无法在压力低时提速,也不能在压力高时及时刹停。正确的做法是将节流与实时监控指标绑定,使工具具备自适应能力。
# 当从库延迟超过 1 秒或主库活跃线程数大于 50 时暂停 pt-online-schema-change --alter="MODIFY COLUMN amount decimal(12,2)" --chunk-size=500 --throttle-method=slave-lag --max-lag=1 --throttle="SHOW GLOBAL STATUS LIKE 'Threads_running'" --throttle-value=50 D=test,t:payments
组合调优的实战对比
我们在一张约两千万行的订单表上做过一组对照。默认参数(chunk-size 10000,无 throttle)下,从库延迟在拷贝期间峰值达到四十秒,主库 CPU 使用率冲到百分之八十五。调整为 chunk-size 500 并启用基于从库延迟的 throttle 后,从库延迟稳定在两秒以内,主库 CPU 峰值降至百分之四十五,整体变更耗时虽增加约三成,但业务侧完全无感知。
下表列出了关键指标变化:
| 配置方案 | chunk-size | throttle | 从库峰值延迟 | 主库 CPU 峰值 |
|---|---|---|---|---|
| 默认 | 10000 | 无 | 40s | 85% |
| 调优后 | 500 | slave-lag=1s | 2s | 45% |
从数据可以看出,适当减小 chunk-size 配合延迟感知的 throttle,是用时间换稳定性的典型权衡。对于核心交易表,这种牺牲部分变更速度来保住线上 SLA 的做法非常值得。
常见误区与避坑建议
一个常见误区是把 throttle 简单写成固定休眠,例如只加 --sleep=1,认为这样就能限流。实际上固定休眠不会看数据库真实负载,低峰期也在慢吞吞拷,高峰期却可能睡得不够久。应当优先使用工具内置的 slave-lag 或 status 条件,或者编写外部检查脚本返回布尔值。
另一个坑是忽略触发器带来的写放大。即使 chunk-size 调小,原表上的高并发写入仍会通过触发器同步到新表,若此时再叠加大量拷贝,可能形成恶性循环。建议在变更前评估业务写入峰值,并在必要时结合业务低峰窗口执行。以下代码展示了一个自定义 throttle 脚本思路:
#!/usr/bin/perl
# 检查从库延迟,超过 2 秒则返回真(触发暂停)
use DBI;
my $dsn = "DBI:mysql:database=test;host=127.0.0.1";
my $dbh = DBI->connect($dsn, 'monitor', 'pwd', {RaiseError => 1});
my $sth = $dbh->prepare("SHOW SLAVE STATUS");
$sth->execute();
my $row = $sth->fetchrow_hashref();
my $lag = $row->{"Seconds_Behind_Master"} || 0;
exit($lag > 2 ? 0 : 1);
将该脚本通过 --throttle-command 接入,工具便能在从库延迟超阈时自动挂起。掌握 chunk-size 与 throttle 的协同,才能让 pt-online-schema-change 真正安全地服务于生产环境的大表变更。
pt-online-schema-changechunk-sizethrottle修改时间:2026-08-08 01:57:34