Django migrations 出现 OperationalError 锁表通常是因为数据库正在执行耗时较长的 DDL 操作(如 ALTER TABLE),导致后续请求无法获取锁。最推荐的处理方向是先终止阻塞会话解除锁表,再优化迁移文件避免大表直接变更,生产环境需严格避免在业务高峰期执行迁移。
先说结论:OperationalError 锁表本质是数据库会话冲突,需立即终止阻塞进程并优化迁移策略。
- 先确认:查看数据库进程列表定位持有锁的会话 ID
- 先处理:终止阻塞查询或回滚当前迁移操作
- 再验证:检查 django_migrations 表状态并重试迁移
命令速用版
以下命令用于快速查看和清理数据库锁,适用于 MySQL 和 PostgreSQL 常见场景。
# MySQL 查看正在运行的进程
SHOW PROCESSLIST;
# MySQL 终止阻塞会话(替换 ID)
KILL QUERY [process_id];
# PostgreSQL 查看锁等待
SELECT pid, usename, query FROM pg_stat_activity WHERE wait_event_type = 'Lock';
# Django 回滚迁移到上一版本
python manage.py migrate [app_name] [previous_migration_name]为什么会这样
锁表是因为迁移操作触发了数据库层面的 DDL 锁,阻塞了正常的 DML 请求。
Django 执行 migrate 时,若涉及添加字段、修改索引或外键约束,数据库需要对表加锁。在高并发或大表场景下,ALTER TABLE 操作耗时较长,期间其他写入请求会等待锁释放。当等待时间超过数据库配置的 lock_wait_timeout,就会抛出 OperationalError。
分步处理
按顺序执行以下步骤,确保数据一致性优先。
- 定位阻塞源:登录数据库执行 SHOW PROCESSLIST 或 pg_stat_activity,找到 State 为 Locked 或等待锁的进程。
- 解除锁持:若是非关键业务进程,使用 KILL 命令终止会话;若是迁移本身卡住,需在 Django 端中断进程。
- 清理迁移状态:检查 django_migrations 表,若当前迁移记录已写入但失败,需手动删除该记录或回滚到上一版本。
- 优化迁移文件:将大表变更拆分为多个小迁移,或使用 RunPython 分批处理数据,避免单次 DDL 耗时过长。
- 重试迁移:在业务低峰期重新执行 python manage.py migrate。
怎么验证是否生效
通过数据库表结构和 Django 迁移记录双重确认。
- 执行 SHOW TABLES 或 \d 表名,确认新字段或索引已存在。
- 查询 django_migrations 表,确认最新迁移记录状态为成功。
- 运行 Django 项目,尝试对涉及表进行写入操作,确认无报错。
常见坑
- 生产环境直接运行 migrate:大表变更应使用零停机迁移方案,如先加 nullable 字段再填充数据。
- 忽略外键约束:添加外键会锁住两张表,需确保关联表无大量写入。
- 迁移文件冲突:多人协作时迁移文件序号冲突会导致状态不一致,需合并迁移文件。
常见问题
生产环境如何安全执行迁移?
建议在业务低峰期执行,并使用数据库备份工具提前快照。
迁移失败后数据会回滚吗?
Django 迁移默认在事务中执行,失败会自动回滚,但 DDL 在某些数据库中不可回滚。
如何避免大表锁表?
使用 django-zero-downtime-migrations 插件或手动分步执行 ALTER 操作。