在线 DDL
先判断可接受的影响
执行 ALTER TABLE 前,先定义四项验收条件:
- 允许的表级锁时间;
- 应用能够承受的延迟、CPU、IO 和临时空间峰值;
- 复制延迟上限;
- 失败后的回滚方式和切换窗口。
“在线 DDL”不等于“无阻塞 DDL”。所有变更通常都需要先取得 Metadata Lock;已存在的长事务或长查询可能阻止 DDL 开始,DDL 开始后排队的事务也可能持续堆积。
选择执行方式
原生 ALTER TABLE
优先评估服务端算法,因为它不引入触发器、影子表或额外复制任务。
sql
-- 指定算法和锁级别;不支持时服务端会报错,不会静默降级
ALTER TABLE users
ADD COLUMN age INT NULL,
ALGORITHM=INSTANT,
LOCK=NONE;不要无条件使用这条语句。目标操作必须在当前服务端版本支持 INSTANT 和 LOCK=NONE,且列属性、行格式、分区和其他表特性满足官方 Online DDL 支持矩阵。
算法边界
| 算法 | 适用行为 | 主要边界 |
|---|---|---|
INSTANT | 对满足条件的元数据修改直接更新表定义 | 版本、操作类型和行格式限制;仍可能等待 Metadata Lock,不代表业务查询一定瞬间完成 |
INPLACE | 在原表基础上重建索引或重建表结构,避免完整复制为新表 | 可能产生大量 IO、临时空间和后台负载;部分操作不支持并发读写 |
COPY | 创建新表并复制数据 | 资源消耗和阻塞风险通常最高;需要更严格的窗口和回滚方案 |
同一个 ALTER 语句在不同 MySQL 小版本、表引擎、列类型和现有表特性下可能支持不同算法。以目标版本的官方支持矩阵和预发布实测为准。
LOCK=NONE 只表示请求表级并发写入,不表示:
- 不需要 Metadata Lock;
- 没有读写延迟或队列阻塞;
- 从库没有复制延迟;
- 磁盘、临时空间和 IO 不会成为瓶颈。
如果显式指定的算法或锁级别不可用,语句会失败。失败本身是有用的保护,不要用 ALGORITHM=DEFAULT 掩盖对结果的未知,再按运行时结果决定是否接受。
选择 pt-osc
pt-online-schema-change 适用于原生 DDL 不能满足锁或资源要求、且业务允许触发器和影子表机制的场景。
bash
# 先 dry-run,检查触发器、主键、复制和负载条件
pt-online-schema-change \
--alter "ADD COLUMN age INT NULL" \
--dry-run \
D=mydb,t=users
# 演练通过后再去掉 --dry-run,并加入实际凭据配置
pt-online-schema-change \
--alter "ADD COLUMN age INT NULL" \
--max-load "Threads_running=25" \
--critical-load "Threads_running=50" \
--max-lag 1 \
--execute \
D=mydb,t=users该工具会创建影子表和触发器,因此需要可靠主键,并评估触发器冲突、Binlog 流量、外键、已有触发器和清理步骤。
选择 gh-ost
gh-ost 通过 Binlog 捕获变更,不使用触发器;前提是目标表有可靠主键,复制链路和 Binlog 格式满足工具要求。
bash
gh-ost \
--host=192.0.2.10 \
--database=mydb \
--table=users \
--alter="ADD COLUMN age INT NULL" \
--max-lag-millis=1500 \
--max-load="Threads_running=25" \
--critical-load="Threads_running=50" \
--dry-rungh-ost 可以暂停、限流或取消,但仍需监控复制延迟、追赶速度、切换时间和清理任务。删除影子表或旧表前先确认切换已经成功。
常见变更的评估
| 变更 | 不应假设的结论 | 首查项 |
|---|---|---|
| 新增或删除索引 | “大表一定瞬间完成” | 索引大小、主键、IO、临时空间和 Metadata Lock |
| 新增或删除列 | “所有 8.0 版本都支持 INSTANT” | 目标小版本、行格式、默认值、位置限制和唯一约束 |
| 修改列类型或长度 | “类型相似就不复制” | 是否可无损转换、字符集变化、现有数据范围 |
| 修改主键 | “只是改索引” | 重复值、全表重建、应用读写路径和切换方式 |
| 转换字符集或排序规则 | “只会更新元数据” | 比较语义、索引长度、排序结果和应用输出 |
| 分区表变更 | “与普通 InnoDB 表相同” | 分区数量、交换/重建路径和复制行为 |
风险表不能替代官方支持矩阵。无法确定时,先复制生产规模和数据分布,在预发布执行同一条 ALTER 并观察锁、IO、磁盘、复制延迟和错误日志。
执行清单
变更前
- 记录表大小、索引、行格式、版本、当前 DDL 和复制拓扑;
- 清理长事务和长查询,确认 Metadata Lock 可以取得;
- 估算临时空间和 Binlog 增量,确保磁盘有余量;
- 在同版本、同规模环境完成 dry-run 或完整演练;
- 准备暂停、取消、失败清理和业务回滚方案;
- 设置复制延迟、连接积压、CPU、IO 和磁盘告警。
变更中
- 持续观察 Metadata Lock、活跃 DDL、复制延迟和目标表状态;
- 达到预设阈值时暂停或限流,不要只看执行是否仍“进行中”;
- 记录每个阶段的开始时间、影响范围和异常信息。
变更后
- 核对
SHOW CREATE TABLE、索引、约束和数据样本; - 执行核心查询和写入冒烟测试;
- 确认从库已追平,Binlog 和备份策略正常;
- 稳定后再删除触发器、影子表或旧表。
最佳实践
- 先判断算法和锁条件:版本与操作类型共同决定可行方案。
- 把 Metadata Lock 纳入窗口:
LOCK=NONE不能绕过事务和查询排队。 - 先演练再生产执行:用真实数据量验证资源峰值和清理流程。
- 设置可触发的阈值:复制延迟、线程数、IO 和磁盘都要能自动暂停或告警。
- 保留回滚路径:影子表、旧表和复制任务在稳定期前不要清理。
- 同时验证正确性与可用性:结构正确不代表业务结果和延迟都可接受。