开放问答
MySQL 1.2 亿行大表加索引,pt-osc / gh-ost / 原生 ALTER 三选一,锁等待和延迟怎么防
该问题的正文、回答与评论。
提问者 陈望舒 · 状态 open
MySQLOnline DDLpt-oscgh-ost大表索引
背景:MySQL 8.0.36,InnoDB,订单流水表 t_settlement 约 1.2 亿行,PK=id,现有 3 个二级索引(idx_merchant_time、idx_status、idx_batch_no)。业务要求新增复合索引 (merchant_id, settled_at, status) 用于结算对账查询,预估索引体积 8–12 GB。
现象(三种方案各踩了一遍坑):
- 原生 ALTER TABLE ... ADD INDEX(ALGORITHM=INPLACE, LOCK=NONE):执行到约 60% 时 MDL 等待队列堆积,业务写入 TPS 从 3200 骤降到 400,持续约 40 秒后恢复。能跑完但锁等待尖刺不可控,我不敢再裸跑了。
- pt-online-schema-change:触发器回放阶段撞上凌晨结算高峰(写入 QPS 峰值 12000),主库本身没崩,但从库复制延迟飙到 22 s,被 SRE 告警强制中止。
- gh-ost:binlog 回放比 pt-osc 略平滑,但 chunk-size 调到 2000 后单批耗时约 8 s,总预估 40+ 小时,业务方不接受超过 8 小时窗口。
已尝试的排查:
- SHOW PROCESSLIST / performance_schema.metadata_locks 确认原生 ALTER 的锁等待来自 copy 阶段的 implicit lock 升级;
- pt-osc 的 --chunk-time=0.2s 和 --max-lag=5s 都加了,但触发器本身在高峰期的写放大没法完全消除;
- gh-ost 的 --max-lag-millis=1500、--chunk-size=2000、--throttle-flag-file 都试了,核心矛盾是 chunk 太小批次数爆炸、太大单批锁持有时间长。
想得到的答案形态:
- 在「1.2 亿行、写入 TPS 3000~12000 波动、全程 P99 < 200 ms、变更窗口 ≤ 8 小时」这组硬约束下,原生 ALTER / pt-osc / gh-ost 的推荐排序与理由,尤其是「哪个方案在高峰写入下最不容易出事」;
- 如果最终选 gh-ost,chunk-size、max-lag-millis、throttle、initially-copy 这几个关键参数在 1 亿行级 + 高写入场景下的具体取值建议,最好有实际跑过的耗时数据;
- 有没有更稳的「影子表 / 从库预建索引再切换」路径?具体步骤、切换瞬间的一致性怎么保证、以及这条路最容易踩的 1–2 个坑。
已采纳
回答者
回答正文
评论
暂无评论。