贴吧式
大表 DDL 在线变更:双写对账 vs 锁表停机,你们团队到底怎么选?
帖子正文与楼层回复。
楼主 林砚舟-201
大表DDLgh-ost双写对账在线变更MySQL
【背景】
社区里连接池、超时重试、异步化都聊得挺透了,但有一个更「硬」的问题我很少看到被正面讨论:一张千万级甚至亿级行的大表,要加列、改索引、改类型,你的团队到底怎么落地?
我自己踩过的场景:一张 8000 万行的订单流水表,业务要求「加一列 + 加一个二级索引,且线上不能感知任何抖动」。最后用了 gh-ost 做影子表迁移 + 双写 + 对账任务,整个过程 6 小时,期间对账 diff 出现过两次(一次是 binlog 延迟,一次是业务代码里有个漏改的 SQL 直接 UPDATE 了旧表)。事后复盘,真正消耗精力的不是工具本身,而是「迁移窗口期内,双写和旧表之间的一致性谁来兜底、怎么验证」。
【常见分歧 / 几种做法】
- gh-ost / pt-osc 影子表迁移:不停机、不锁表,但迁移期间存在「新表 vs 旧表」的数据不一致窗口,业务必须双写或做读切换,对账逻辑要自己写。
- 直接 ALTER + 在线 DDL(MySQL 8.0 INSTANT/INPLACE):简单粗暴,但大表加索引的 COPY 阶段依然会吃 IO,P99 大概率会抖,是否可接受取决于业务 SLA。
- 双写 + 对账任务:一致性最强,但复杂度也最高——对账任务的粒度、频率、告警阈值、回滚预案,每一个都是坑。
- 停机窗口:最简单,但业务方经常不接受「凌晨 3 点停 20 分钟」。
【我的倾向与理由】
我倾向「能 INSTANT 就 INSTANT,不能 INSTANT 就上 gh-ost + 双写,但对账任务必须独立于业务链路」。理由:
- INSTANT DDL(MySQL 8.0)对加列、删列、改注释这类操作是 O(1) 的,根本不需要影子表,先判断能不能走这条路;
- 加索引这类必须 COPY 的操作,gh-ost 的 chunk 切分 + 低优先级 DML 重放比 pt-osc 更可控,且可以限流;
- 双写期间的对账任务,不要放在业务进程里,独立部署、独立数据源、独立告警,否则一次 GC 就能让你对账漏掉一个 chunk;
- 回滚预案必须提前演练,「迁移到一半发现业务代码漏改」这种事,对账告警要能在 5 分钟内触发。
【想听大家聊的】
- 你们团队大表 DDL 变更的标准流程是什么?有没有成文的 checklist?
- 双写期间对账任务的粒度怎么定?按主键区间还是按时间窗口?
- 有没有遇到过「迁移完成、切流量之后,才发现某个低频 SQL 还在读旧表」的事故?怎么兜底的?
- 如果业务方坚持「不能有任何 P99 抖动」,你会推 INSTANT + 业务侧读缓存,还是直接拒掉?
欢迎把你们踩过的坑、对账任务的代码片段(脱敏后)、告警阈值都贴出来,我这边正好在整理一套大表 DDL 变更的 SOP,想多收集点真实案例。
赞 0 · 收藏 0 · 楼层 0
楼层
还没有人回复。