Django的migrate命令本身不锁表,锁表源于底层数据库执行ALTER TABLE等DDL语句时的固有行为;PostgreSQL 11+对无默认值ADD COLUMN支持并发,但带DEFAULT仍需重写表,MySQL多数ALTER触发copy-alter流程导致锁表。

migrate 命令在生产环境锁表,不是 Django 故意设计的“功能”,而是底层数据库(如 PostgreSQL、MySQL)执行 DDL 操作时的自然行为——多数 ALTER TABLE 操作会阻塞读写,尤其在大表上持续数秒甚至数分钟。
这背后没有魔法,只有 SQL 和事务隔离级别的现实约束。下面说清楚怎么做、为什么、以及最容易翻车的地方。
ALTER TABLE 在 PostgreSQL/MySQL 中天然锁表
当你加字段、改类型、设默认值时,Django 生成的迁移最终会调用类似 ALTER TABLE users ADD COLUMN is_active BOOLEAN DEFAULT TRUE 的语句。这类操作在:
- PostgreSQL 11+ 对
ADD COLUMN且无默认值或默认为 NULL 的情况支持并发(不锁写),但带DEFAULT或NOT NULL仍需全表扫描并重写数据页 - MySQL(InnoDB)几乎所有
ALTER TABLE都触发“copy-alter”流程:建新表 → 拷贝数据 → 重命名,期间原表只读或完全锁定(取决于版本和ALGORITHM参数) - SQLite 直接串行化所有写操作,锁表现象更明显
所以不是 Django 锁表,是它生成的 SQL 让数据库锁了。
立即学习“Python免费学习笔记(深入)”;
migrate 不会自动拆分或后台执行 DDL
Django 的 migrate 是同步阻塞命令,它:
仅使用 Python 将 Claw Code 虚拟化框架与 OpenClaw 集成,提供 184 种工具和 200 多条命令,用于安全分析和代码质量...
- 按顺序执行每个迁移文件中的
Operation - 每个
Operation转成一条或多条 SQL,在一个事务里提交(部分数据库不支持 DDL 在事务中) - 不做在线 DDL 封装,也不调用 pt-online-schema-change 或 pg_repack 这类工具
也就是说,你跑 python manage.py migrate,就等于手动在 psql 或 mysql client 里敲那几条 ALTER,没有任何缓冲或降级策略。
如何避免锁表?关键在迁移写法和执行时机
真正能缓解锁表的,不是换命令,而是改写法 + 控制执行节奏:
- 加字段优先用
null=True,避免default=...;后续再用数据迁移填值(RunPython) - 修改字段类型前,先新增兼容字段 → 同步双写 → 迁移旧数据 → 删除旧字段,而不是直接
AlterField - 对大表(>100万行),把单个迁移拆成多个小迁移:比如先
AddField,再RunSQL批量更新,最后AlterField改约束 - 在低峰期执行,并配合数据库侧的在线 DDL 工具:MySQL 可加
ALGORITHM=INPLACE, LOCK=NONE(需 5.6+),PostgreSQL 可用CONCURRENTLY创建索引(但不适用于 ALTER COLUMN)
注意:--fake 或 --fake-initial 不解决锁表问题,它们跳过 SQL 执行,只更新 django_migrations 表——适合修复迁移状态错乱,但不能绕过真实 DDL。
生产环境迁移失败后残留锁怎么查?
锁本身不会“残留”,但迁移中断可能导致:
- 未提交的长事务(PostgreSQL 中
SELECT * FROM pg_stat_activity WHERE state = 'idle in transaction') - MySQL 中被 kill 掉但未回滚完的 ALTER(查
SHOW PROCESSLIST看 State 是否卡在copy to tmp table) -
django_migrations表记录不全,导致下次migrate重复尝试已部分执行的迁移
此时不要删迁移文件硬重来。先确认数据库是否真有锁,再决定是手动清理事务、补 fake 记录,还是用 sqlmigrate 提取 SQL 单独执行并监控。
真正麻烦的从来不是“怎么跑迁移”,而是“哪条 SQL 会锁、锁多久、有没有替代路径”。DDL 没有银弹,只有权衡。

















