线上SQL变更会锁表或阻塞查询,因MySQL 5.7及更早版本ALTER TABLE默认表级拷贝,写入挂起;MySQL 8.0 RENAME COLUMN虽不拷贝数据但需SX元数据锁;PostgreSQL ADD COLUMN带DEFAULT非NULL时仍可能锁表;主从复制中DDL易引发延迟、不一致与隐性失败。

线上 SQL 变更为何会锁表或阻塞查询?
MySQL 5.7 及更早版本中,ALTER TABLE 大部分操作默认使用「表级拷贝」方式:先建新表、逐行复制数据、重建索引、再原子替换。整个过程原表持续可读,但写入(INSERT/UPDATE/DELETE)会被挂起,直到拷贝完成。如果表有 5000 万行,拷贝耗时 20 分钟,那这期间所有 DML 都在等待 waiting for table metadata lock。
-
ADD COLUMN、DROP COLUMN、MODIFY COLUMN在未指定ALGORITHM=INPLACE且不满足就地修改条件时,都会触发全表拷贝 - 即使是
ADD INDEX,在 MySQL 5.6+ 虽支持INPLACE,但若索引字段含TEXT/BLOB或字符集不一致,仍会退化为拷贝模式 -
pt-online-schema-change或gh-ost是绕过锁的常用方案,但它们本身会持续读写主表,对从库延迟、binlog 网络压力、主从 GTID 一致性都有隐性影响
为什么 ALTER TABLE ... RENAME COLUMN 在 MySQL 8.0 也需谨慎?
MySQL 8.0 引入了原生 RENAME COLUMN,语法简洁,但底层仍需重写表元数据并更新所有相关统计信息。它虽不拷贝数据,却会在执行瞬间获取 SX(Shared-Exclusive)元数据锁,阻塞并发的 SELECT ... FOR UPDATE、CREATE INDEX 等操作。
- 如果该列被视图、存储过程、触发器、分区表达式引用,
RENAME COLUMN会直接失败,报错ERROR 3780 (HY000) - 使用
information_schema.COLUMNS或 ORM 自动生成建表语句的系统,可能因列名变更导致后续迁移脚本比对异常 - 该操作不可回滚:一旦成功,旧列名彻底消失,应用若未同步更新,将立即抛出
Unknown column 'xxx' in 'field list'
PostgreSQL 的 ALTER TABLE ... ADD COLUMN 真的完全无锁?
PostgreSQL 对大多数 ADD COLUMN 操作确实只持 ACCESS EXCLUSIVE 锁极短时间(仅更新系统表),之后即可并发读写。但这不等于“零风险”:
- 若新增列带
DEFAULT值且非NULL,PostgreSQL 11 之前会触发全表扫描补值(即“rewrite table”),锁表时间与数据量正相关 - PostgreSQL 12+ 支持
ADD COLUMN ... DEFAULT NULL无 rewrite,但若后续执行ALTER COLUMN SET DEFAULT并立刻UPDATE补值,等效于一次全表更新,极易引发长事务和 WAL 膨胀 -
pg_stat_progress_alter_table视图可监控进度,但线上环境往往没开track_activities,无法及时感知卡住
DDL 变更如何意外破坏主从一致性?
MySQL 主从复制中,ALTER TABLE 属于 DDL,其执行逻辑在 binlog 中记录为单条事件,但实际在从库回放时,仍要走一遍相同流程。问题常出现在:
- 从库负载高或 IO 延迟大,导致 DDL 回放滞后,在此期间主库已执行后续 DML,造成从库短暂不一致甚至复制中断(如
Slave_SQL_Running_State: Waiting for table metadata lock) - 使用
STATEMENT格式 binlog 时,某些函数(如NOW()、UUID())在从库重放结果不同,而 DDL 中若嵌套这类表达式(如生成列定义),会导致主从表结构实质差异 -
ALTER TABLE ... ENGINE=InnoDB这类操作在从库可能因磁盘空间不足静默失败,错误日志只记Got error 12 from storage engine,不触发复制停止,隐患极深
线上 DDL 最危险的不是它慢,而是它“看起来快、实际副作用散落各处”——锁表现象易察觉,但元数据不一致、复制延迟毛刺、统计信息陈旧、ORM 缓存错位这些,往往在变更后数小时才集中爆发。










