线上大表加索引 / 改字段,你更信原生 INPLACE、pt-osc 还是 gh-ost?
帖子正文与楼层回复。
背景先说清楚:我这边一张 InnoDB 流水表 1.5 亿行,业务方临时要加一个二级索引 + 一个 NOT NULL 默认值的字段,要求「不停机、对线上 P99 影响可控、可回滚」。这种需求在大数据平台里太常见了,但每次拉群对齐,方案都打架,所以想借这个帖子把分歧摆到台面上。
社区里我看到的常见做法大致三类:
一、原生路线:直接 ALTER TABLE ... ALGORITHM=INPLACE, LOCK=NONE,靠 MySQL 5.6+ 的 online DDL 自己扛。优点是零依赖、语义最干净;缺点是 md5/char 这类列变更、加主键、改字符集这些场景会悄悄退化成 COPY + 全表锁,而且 MDL 排队一旦撞上长事务,所有 SELECT 全堵死,回滚比做变更还痛。
二、pt-osc 路线:建影子表 + trigger 同步,分批 copy 再 rename。优点是老牌、可控、能限速;缺点是 trigger 在 DML 高峰期会给写入加额外开销,rename 那一刻仍然要拿 MDL,遇到长事务照样翻车,而且它不感知 binlog 位点,主从延迟会被放大。
三、gh-ost 路线:直接解析 binlog 做增量同步,无 trigger,rename 前可暂停。优点是 DML 几乎零侵入、可暂停可限速;缺点是依赖 binlog 权限与格式(ROW),多主 / 半同步拓扑下要小心,且工具本身需要常驻进程,运维成本不低。
我的倾向:默认走原生 INPLACE,能用就用;一旦预估锁表时间 > 30 秒或者表上有持续 DML,直接上 gh-ost;pt-osc 只在 gh-ost 跑不通(binlog 受限)时兜底。理由有三:一是 gh-ost 不改主表 DML 路径,对 P99 最友好;二是它支持 --cut-over=atomic|two-step,rename 窗口可以压到毫秒级;三是它的暂停/恢复语义比 pt-osc 更适合「业务高峰先停、凌晨再续」这种节奏。
但我不想把结论说死,特别想听几种场景下的经验:
- 你们在 10 亿行以上的表上做过加索引吗?用的什么方案,实际耗时和主从延迟各是多少?
- gh-ost 的 binlog 解析在 binlog 量特别大(比如每秒几千 DML)时,追赶速度跟得上吗?有没有被「同步速度 < 写入速度」卡死过的?
- pt-osc 的 trigger 方案在写入密集的表上,你们观察到 P99 涨了多少?有没有量化数据?
- 云厂商托管库(RDS/PolarDB 这类)原生 DDL 已经做得比较稳了,你们还会坚持用 gh-ost 吗,还是直接信云厂商?
- 有没有人踩过「INPLACE 改字段类型结果退化成 COPY」然后线上被锁死的坑?想听复盘。
欢迎带具体数据来聊(表大小、DML QPS、实际耗时、延迟),空对空的「我们用 XX 工具」对讨论帮助不大。