MySQL 8.0.12+多数DDL默认支持在线(ALGORITHM=INSTANT/INPLACE),但修改主键、CHAR长度收缩、非空表加全文索引等仍会锁表;不满足LOCK=NONE时需降级或改用pt-osc。

MySQL 8.0+ 的 ALTER TABLE 默认是否在线?
MySQL 8.0.12 起,多数 DDL 操作(如加列、改列类型、加索引)默认启用 ALGORITHM=INSTANT 或 INPLACE,只要满足条件就不锁表。但「默认在线」不等于「绝对不锁」——比如修改主键、变更列的 CHAR 长度、或在非空表上加全文索引,仍会触发 COPY 算法,全程锁表。
判断方式很简单:执行 ALTER TABLE ... ALGORITHM=INPLACE, LOCK=NONE;,如果报错提示 LOCK=NONE is not supported,说明该操作无法完全无锁,需降级为 LOCK=SHARED(允许读)或接受短暂 LOCK=EXCLUSIVE(读写全阻塞)。
- 用
SHOW CREATE TABLE确认表引擎是InnoDB(MyISAM 不支持 Online DDL) - 避免在
TEXT/BLOB列上做长度收缩,容易退化为COPY - 加索引时优先用
ADD INDEX而非MODIFY COLUMN,后者更易触发重建
什么时候必须用 pt-online-schema-change?
当 Online DDL 失效、或你无法接受任何写入延迟(哪怕几毫秒的 LOCK=SHARED)时,pt-osc 是更稳妥的选择。它本质是「影子表 + 触发器 + 增量同步」,全程不锁原表,但代价是双倍磁盘空间、更高的 CPU/IO 开销,且对高并发写入敏感。
典型必须上 pt-osc 的场景:
- MySQL 5.7 或更早版本(Online DDL 能力弱,
ADD COLUMN在中间位置仍锁表) - 需要原子性地重命名列(
CHANGE COLUMN)且不能停写 - 目标表有外键约束(Online DDL 对外键支持有限,
pt-osc会自动处理) - DBA 明确要求「零
Waiting for table metadata lock」——比如金融核心库的凌晨维护窗口极短
注意:pt-osc 不兼容 GTID 模式下开启 enforce_gtid_consistency=ON 且存在匿名事务的实例;运行前务必检查 SELECT @@gtid_mode, @@enforce_gtid_consistency;。
pt-osc 执行中为什么会出现主从延迟暴增?
根本原因是触发器在原表每条 INSERT/UPDATE/DELETE 后,额外发起一次对影子表的操作,相当于写放大。若原表 QPS 高、单行数据大、或从库 SQL 线程单线程瓶颈明显,延迟就会快速累积。
缓解方法不是调大 --chunk-size,而是反向控制节奏:
- 用
--max-load="Threads_running=25"主动限流,当从库Threads_running超过阈值就暂停拷贝 - 避开业务高峰,尤其避免在从库已积压 relay log 时启动
- 确认从库
slave_parallel_workers > 0且使用LOGICAL_CLOCK并行模式 - 禁用
--drop-new-table(默认关闭),防止意外中断后删错表
执行中可随时 KILL pt-osc 进程,它会自动回滚(删影子表、恢复原表触发器),但已同步的数据不会回退——这点和原生 ALTER 的原子性不同。
DDL 变更后如何验证数据一致性?
pt-osc 自带 --dry-run 和 --print,但真正上线前必须做校验。别依赖「没报错就成功」——触发器漏事件、网络闪断、磁盘满都可能导致影子表少数据。
- 用
pt-table-checksum对比原表和新表的校验和(需先等pt-osc完成RENAME,再对切换后的表名校验) - 抽样查关键业务字段:比如
SELECT COUNT(*), SUM(amount) FROM t_order WHERE created_at > '2024-01-01'在新旧表执行并比对 - 检查索引是否生效:
EXPLAIN SELECT * FROM t_order WHERE user_id = 123;确认走了新索引
最容易被忽略的是触发器残留:执行完 pt-osc 后,用 SHOW TRIGGERS LIKE 't_order' 确保无 _ptosc 相关触发器;若有,手动 DROP TRIGGER,否则下次 DDL 可能冲突。


















