MySQL 小表加字段通常没什么感觉,一条 alter table add column 很快结束。但如果表有几千万、上亿行,DDL 就变成一次生产变更。它可能锁表、撑爆临时空间、制造主从延迟,甚至拖慢线上写入。
所以大表在线 DDL 不是“会不会写 SQL”的问题,而是变更风险控制问题。
先确认表和业务状态
动大表前,至少要确认这些信息:
- 表数据量和索引数量。
- 当前 MySQL 版本。
- 存储引擎是否为 InnoDB。
- 表上有没有外键和触发器。
- 当前有没有大事务。
- 磁盘空间是否足够。
- 主从复制延迟是否稳定。
- 业务低峰窗口在哪里。
这些信息会影响 DDL 方式选择。如果表很小,直接执行就行;如果表巨大,就要考虑在线 DDL、从库先变更、pt-online-schema-change 或 gh-ost 等方案。
MySQL 原始 DDL 为什么危险
早期 InnoDB 做 DDL 时,可能会创建临时表,把原表数据复制过去,再 rename 替换。这个过程会涉及大量 IO、临时空间和锁。
简化流程类似:
- 按新结构创建临时表。
- 从原表复制数据到临时表。
- 维护索引。
- rename 新旧表。
- 删除旧表。
如果过程中持有写锁,线上写入就会被阻塞。即使支持 Online DDL,也要看具体操作是否真的能 INPLACE、是否允许 LOCK=NONE。
Online DDL 也要看算法和锁
MySQL DDL 常见参数包括:
alter table t
add column c varchar(32),
algorithm=inplace,
lock=none;
需要关注:
algorithm=copy:通常需要复制表,风险大。algorithm=inplace:尽量原地变更,但不是所有操作都支持。lock=none:尽量不阻塞读写。lock=shared:允许读,限制写。lock=exclusive:读写都可能被阻塞。
不要只看到“Online DDL”四个字就放心。真正执行前要结合版本、操作类型和执行计划判断。
主从架构下要考虑复制延迟
如果直接在主库上执行大 DDL,主库执行完后,binlog 会传给从库。从库 SQL 线程通常按顺序执行,DDL 如果耗时很久,后面的复制事件就会排队,主从延迟可能迅速变大。
一种思路是先在从库变更,再做主从切换,最后处理原主库。大致步骤是:
- 停止从库复制。
- 从库关闭 binlog 或避免写入复制链路。
- 在从库执行 DDL。
- 恢复复制并观察延迟。
- 做主从切换。
- 在原主库执行同样 DDL。
这类方案要配合成熟的主从切换工具和回滚预案,不适合临时手工乱操作。
pt-online-schema-change 的原理
pt-online-schema-change 是经典在线改表工具。它的核心不是直接改原表,而是创建一张新表,然后慢慢搬数据。
大致流程:
- 创建一张和原表结构相同的新表。
- 在新表上执行目标 DDL。
- 在原表上创建 insert、update、delete 触发器。
- 分批把原表数据复制到新表。
- 复制过程中用触发器同步增量变更。
- 最后 rename 表,把新表替换成原表。
- 删除触发器和旧表。
它的好处是降低长时间锁表风险,坏处是引入触发器、额外写入、额外空间和 rename 切换风险。
pt-osc 不是无脑安全
pt-osc 有几个常见限制:
- 表上已有触发器时会冲突。
- 外键表需要额外参数和判断。
- 表写入压力大时,触发器会增加负担。
- 需要额外磁盘空间保存新表。
- rename 切换瞬间仍需要元数据锁。
所以用 pt-osc 前,要先 dry-run,并确认主键、外键、触发器、磁盘、复制延迟和业务峰值。
加字段前的检查清单
一个比较稳的检查顺序:
- 确认字段是否真的需要立即加。
- 确认默认值是否会触发表重写。
- 确认字段是否允许 NULL。
- 确认是否需要同时加索引。
- 确认 DDL 算法和锁级别。
- 确认磁盘空间和临时目录。
- 确认主从延迟和大事务。
- 准备回滚和暂停策略。
- 低峰执行。
- 执行后观察慢查询、复制延迟和业务错误。
字段变更不要和索引变更、大批量回填、代码发布混在一次不可控变更里。能拆就拆,先加 nullable 字段,再灰度写入,再回填,再加约束。
最后看取舍
小表直接改,大表先评估。Online DDL、pt-osc、从库先变更都不是银弹,它们只是不同风险之间的取舍。
真正重要的是:执行前知道会不会锁、会不会复制延迟、会不会占空间;执行中能观察;执行后能验证;出问题能停下来。




