千万级表加字段必须规避锁表风险:add column带default(text/blob/json)、after定位、生成列、myisam引擎或mysql

ALTER TABLE ADD COLUMN 在千万级表上直接执行,几乎必然导致写入阻塞、主从延迟飙升、甚至服务雪崩。这不是慢不慢的问题,而是“是否允许业务中断”的架构决策。
哪些 ALTER TABLE 操作会退化为锁表?
即使你写了 ALGORITHM=INPLACE, LOCK=NONE,只要触发以下任一条件,MySQL 就会悄悄降级为拷贝全表(即 ALGORITHM=COPY),并持有元数据锁(MDL)直到完成:
-
ADD COLUMN带DEFAULT且字段类型是TEXT/BLOB/JSON - 加字段时指定
AFTER col_name(位置约束) - 字段定义含生成列(
GENERATED ALWAYS AS)、虚拟列或函数默认值(如DEFAULT CURRENT_TIMESTAMP) - 表使用 MyISAM 引擎(InnoDB 才支持 Online DDL)
- MySQL 版本低于 5.6(不支持
LOCK=NONE)
验证是否真正在线:执行前先查 SHOW CREATE TABLE your_table 确认引擎和版本;执行中监控 SELECT * FROM information_schema.INNODB_TRX,若出现长时间未提交事务,说明已卡住。
为什么 pt-online-schema-change 是生产首选?
它绕开了 MySQL DDL 机制本身,用应用层逻辑实现“无锁”——本质是双表+触发器同步,与 MySQL 版本无关,也无需担心 ALGORITHM 退化问题。
但必须注意三个硬性前提:
- 操作用户需有
TRIGGER、ALTER、DROP、INSERT、UPDATE、DELETE权限(缺一不可) - 原表必须有主键或唯一非空索引(否则无法分 chunk 迁移)
- 磁盘剩余空间 ≥ 原表大小(不是 2 倍,因不重建索引,但需存新表+binlog 日志)
典型命令要带限速和校验:
pt-online-schema-change \ --alter "ADD COLUMN status TINYINT DEFAULT 0" \ D=shop,t=orders \ --execute \ --chunk-time=0.3 \ --max-load="Threads_running=15" \ --critical-load="Threads_running=50" \ --check-interval=5
切勿省略 --execute —— 不加它只打印 SQL,不干活;--dry-run 仅用于预演。
没权限装第三方工具?走影子表必须避开的两个坑
手动建影子表迁移,看似可控,实则极易出错。最容易被忽略的致命点有两个:
-
别在影子表上提前建索引或主键:
CREATE TABLE orders_new LIKE orders后立刻执行ALTER TABLE orders_new DROP PRIMARY KEY, DROP INDEX idx_created_at,等所有数据导入完再重建——否则INSERT ... SELECT会因索引维护拖慢 3–5 倍 -
别用
LIMIT分批,要用主键范围切片:比如WHERE id BETWEEN 1000001 AND 2000000,配合SELECT MAX(id) FROM orders动态推进;否则高并发写入下会漏数据或重复
增量同步阶段,必须监听 binlog 或用触发器捕获变更,并在切换前做一次 CHECKSUM TABLE 校验一致性。这一步没人替你做,跳过等于埋雷。
真正安全的加字段,从来不是“选哪个命令”,而是“能否承受中间状态”。哪怕用了 pt-osc,在最后 RENAME 的瞬间仍有毫秒级阻塞;哪怕走影子表,切换那几秒也存在请求打到旧表的风险。这些细节不写进预案,就不是生产可用方案。











