postgresql 11+ 和 mysql 8.0.12+ 中,add column default 常量值(如 0、'a')不锁表、不扫描全表,仅修改系统表元数据;但若 default 为 volatile 函数(如 now()、random()),则 postgresql 回退重写表,mysql 仍可能触发重建。

ALTER TABLE ADD COLUMN 时直接指定 DEFAULT 会锁表吗?
在 PostgreSQL 和 MySQL 8.0.12+ 中,ALTER TABLE ... ADD COLUMN ... DEFAULT 不会全表扫描或锁住整个表写入(前提是默认值是常量,比如 'unknown' 或 0)。PostgreSQL 从 11 开始、MySQL 从 8.0.12 起都支持“元数据级添加列”,即只改系统表,不触碰数据页。但注意:SQL Server 和老版本 MySQL(如 5.7)仍会重建整张表,尤其当表超大时可能卡住数小时。
实操建议:
- 先查清数据库版本和引擎:MySQL 看
SELECT VERSION(), @@default_storage_engine;PostgreSQL 看SELECT version() - 避免用函数作为默认值,例如
NOW()或uuid_generate_v4()—— 这会让 PostgreSQL 回退到逐行计算,触发重写 - 如果必须加带函数默认的列且不能停写,改用两阶段:先加 nullable 列 → 后分批 UPDATE → 最后
ALTER COLUMN SET DEFAULT+ALTER COLUMN SET NOT NULL
UPDATE 大表历史数据为什么慢?怎么分批才不堵主库?
直接 UPDATE big_table SET status = 'active' WHERE status IS NULL 会持有大量行锁、写 WAL/Redo 日志暴涨、可能触发 autovacuum 压力,还容易被 kill 或超时中断。关键不是“要不要分批”,而是“按什么字段分批”和“每批多少行”。
实操建议:
- 优先用主键或高基数索引字段切片,例如
WHERE id BETWEEN 1000000 AND 1009999,避免OFFSET/LIMIT(深度分页效率随偏移量下降) - 每批控制在 1k–5k 行:太小则事务开销占比高;太大则单次锁持有久、复制延迟突增(尤其 MySQL 主从架构)
- 加
AND status IS NULL这类过滤条件必须有对应索引,否则每次扫描全表 —— 可建临时索引:CREATE INDEX CONCURRENTLY ON big_table ((status)) WHERE status IS NULL(PostgreSQL) - MySQL 用户注意:设
SET SESSION innodb_lock_wait_timeout = 30防死等,别依赖全局 timeout
如何验证默认值已生效且历史数据没漏更新?
加完列、跑完 UPDATE 后,光看 SELECT COUNT(*) 不够——NULL 值可能残留,新插入行也可能因触发器或应用层逻辑绕过默认值。必须交叉验证三处。
实操建议:
- 查元数据确认默认值已注册:
SELECT column_default FROM information_schema.columns WHERE table_name = 'big_table' AND column_name = 'status' - 抽样检查新老数据混合结果:
SELECT id, status, created_at FROM big_table WHERE id IN (1, 100000, 999999) ORDER BY id,重点看最早和最新几条是否都非 NULL - 模拟新插入:
INSERT INTO big_table (name) VALUES ('test'),立刻SELECT status FROM big_table WHERE name = 'test',验证是否真用了默认值而非 NULL - 别忘了检查应用代码:ORM 比如 Django 的
default=...或 MyBatis 的useGeneratedKeys可能覆盖数据库默认行为
PostgreSQL vs MySQL 在大表变更中的关键差异点
同样操作,在两个系统里风险点完全不同。比如加默认列后补数据,PostgreSQL 更怕 VACUUM 延迟导致膨胀,MySQL 则更怕 binlog 写满磁盘或主从 GTID 不一致。
实操建议:
- PostgreSQL:补完数据立刻执行
VACUUM ANALYZE big_table,否则后续查询可能走错执行计划;监控pg_stat_progress_vacuum视图 - MySQL:开启
binlog_row_image = FULL(默认),否则部分 UPDATE 可能不记录完整镜像,导致从库回放失败 - 两者都禁用:不要在业务高峰跑大 UPDATE;不要依赖
pt-online-schema-change或pg_repack自动处理“带默认值的列添加”——它们对 DEFAULT 的识别逻辑有盲区,容易漏掉元数据同步










