Django migrations 迁移时遇到 OperationalError 锁表怎么办

文章导读
Django migrations 出现 OperationalError 锁表通常是因为数据库正在执行耗时较长的 DDL 操作(如 ALTER TABLE),导致后续请求无法获取锁。最推荐的处理方向是先终止阻塞会话解除锁表,再优化迁移文件避免大表直接变更,生产环境需严格避免在业务高峰期执行迁移。
📋 目录
  1. 命令速用版
  2. 为什么会这样
  3. 分步处理
  4. 怎么验证是否生效
  5. 常见坑
  6. 常见问题
A A

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。

分步处理

按顺序执行以下步骤,确保数据一致性优先。

Django migrations 迁移时遇到 OperationalError 锁表怎么办
  1. 定位阻塞源:登录数据库执行 SHOW PROCESSLIST 或 pg_stat_activity,找到 State 为 Locked 或等待锁的进程。
  2. 解除锁持:若是非关键业务进程,使用 KILL 命令终止会话;若是迁移本身卡住,需在 Django 端中断进程。
  3. 清理迁移状态:检查 django_migrations 表,若当前迁移记录已写入但失败,需手动删除该记录或回滚到上一版本。
  4. 优化迁移文件:将大表变更拆分为多个小迁移,或使用 RunPython 分批处理数据,避免单次 DDL 耗时过长。
  5. 重试迁移:在业务低峰期重新执行 python manage.py migrate。

怎么验证是否生效

通过数据库表结构和 Django 迁移记录双重确认。

  • 执行 SHOW TABLES 或 \d 表名,确认新字段或索引已存在。
  • 查询 django_migrations 表,确认最新迁移记录状态为成功。
  • 运行 Django 项目,尝试对涉及表进行写入操作,确认无报错。

常见坑

  • 生产环境直接运行 migrate:大表变更应使用零停机迁移方案,如先加 nullable 字段再填充数据。
  • 忽略外键约束:添加外键会锁住两张表,需确保关联表无大量写入。
  • 迁移文件冲突:多人协作时迁移文件序号冲突会导致状态不一致,需合并迁移文件。

常见问题

生产环境如何安全执行迁移?

建议在业务低峰期执行,并使用数据库备份工具提前快照。

迁移失败后数据会回滚吗?

Django 迁移默认在事务中执行,失败会自动回滚,但 DDL 在某些数据库中不可回滚。

如何避免大表锁表?

使用 django-zero-downtime-migrations 插件或手动分步执行 ALTER 操作。