会锁表,且是全表DML锁;MySQL 5.7及以前必锁,8.0+加自增主键也不支持INSTANT算法;PostgreSQL需建序列、设默认值、同步当前值才能安全转SERIAL主键。
MySQL 用 ALTER TABLE ... MODIFY 加自增主键会锁表吗
会,而且是全表 dml 锁(在 innodb_lock_wait_timeout 范围内阻塞 insert/update/delete),不是只读锁。5.7 及以前版本基本没得选;8.0+ 启用 algorithm=instant 才可能免锁,但仅限添加列、重命名列等极少数操作——加自增主键不在其中。
常见错误现象:ALTER TABLE t1 ADD id BIGINT PRIMARY KEY AUTO_INCREMENT FIRST; 在生产表上执行数分钟无响应,监控显示 Waiting for table metadata lock,连 SELECT 都卡住(因元数据锁升级)。
- 必须确认表大小:千万级行以上,别直接跑 DDL,先在从库或影子表验证耗时
- 避免在业务高峰执行;若必须在线,优先考虑
pt-online-schema-change或gh-ost -
AUTO_INCREMENT值不能靠MODIFY自动推导——必须显式指定START WITH或先查最大值再设ALTER TABLE ... AUTO_INCREMENT = N
PostgreSQL 怎么安全地把普通列转成 SERIAL 主键
PostgreSQL 没有真正的 “自增类型”,SERIAL 只是语法糖,底层是 SEQUENCE + DEFAULT。直接 ALTER COLUMN ... TYPE SERIAL 会报错,因为 SERIAL 不是真实类型。
使用场景:已有表带 id 列但无主键和默认值,现在要补上自增逻辑。
- 先建序列:
CREATE SEQUENCE t1_id_seq OWNED BY t1.id; - 设默认值:
ALTER TABLE t1 ALTER COLUMN id SET DEFAULT nextval('t1_id_seq'); - 设主键:
ALTER TABLE t1 ADD PRIMARY KEY (id); - 关键一步:同步序列当前值,否则下次插入可能冲突:
SELECT setval('t1_id_seq', COALESCE((SELECT MAX(id) FROM t1), 0));
注意:OWNED BY 表示该序列被列“拥有”,删列会自动删序列;但若列已存在值,必须手动调用 setval,否则新插入从 1 开始。
生成可回滚的 DDL 脚本要注意哪几个硬性条件
DDL 本身不可事务回滚(MySQL 的 DROP/CREATE 类操作,PostgreSQL 的多数 ALTER 除外),所谓“可回滚”是指人工能快速切回旧结构,不是数据库层面支持 ROLLBACK。
容易踩的坑:脚本里只写了 ADD COLUMN,没写对应 DROP COLUMN;或者主键变更后,没保留原索引名,导致回滚语句执行失败。
- 每个
ADD操作,必须配对写出DROP(如ADD PRIMARY KEY→DROP CONSTRAINT,不是DROP PRIMARY KEY) - 显式命名所有约束:
ADD CONSTRAINT pk_t1_id PRIMARY KEY (id),否则 PostgreSQL 自动生成名如t1_pkey,不同环境不一致 - MySQL 中,
ENGINE=InnoDB和CHARSET=utf8mb4必须显式声明,避免从库因默认值差异导致复制中断
上线前怎么验证 DDL 脚本在目标版本真能跑通
开发环境 MySQL 8.0,生产还是 5.7?PostgreSQL 从 12 升到 15?版本差一个点,JSON 字段行为、GENERATED COLUMN 语法、甚至 IF NOT EXISTS 支持都可能不兼容。
最简单的验证方式:拿生产环境同版本的只读实例(或 mysqldump 出的 CREATE TABLE 语句),在本地起个 Docker 实例,用 mysql -u root -h 127.0.0.1 直接跑。
- 检查错误信息是否含
ERROR 1064(语法错误)、ERROR 1832(MySQL 不支持的 ALGORITHM) - 特别注意注释:MySQL 5.7 不识别
/*+ INVISIBLE */这类优化器提示,会当语法错误 - PostgreSQL 要确认
pg_dump --no-owner --no-privileges导出的建表语句,和你要执行的 DDL 是否字段顺序、约束名完全一致
复杂点在于自增初始值和序列状态必须人工核对,工具不会帮你检查这个。

















