← 返回题目列表

Django Migration 的作用是什么?线上执行迁移要注意什么?

高频 中等 第 12 / 27 题 更新于 2026/07/27
DjangoMigration数据库变更部署

简化版

Django Migration 用来把模型变更转成可追踪、可执行、可回滚的数据库结构变更。开发时用 makemigrations 生成迁移文件,用 migrate 应用迁移。线上要注意备份、兼容发布、避免大表长时间锁表、不要随意改已经上线的迁移文件。

详细版

Migration 的核心作用:

  • 记录模型结构变化;
  • 生成数据库 DDL;
  • 保证不同环境结构一致;
  • 支持按依赖顺序执行;
  • 支持一定程度的回滚。

常用命令:

python manage.py makemigrations
python manage.py migrate
python manage.py showmigrations
python manage.py sqlmigrate app_name 0001

线上迁移要特别谨慎:

  • 先备份数据库;
  • 评估大表加字段、建索引、改字段类型的锁表风险;
  • 使用向前兼容的灰度发布流程;
  • 数据迁移和结构迁移尽量拆开;
  • 已经合入并在线上执行过的迁移不要随意修改,应该新增迁移。

完整版教学

一、Migration 解决了什么问题

没有迁移系统时,团队容易遇到这些问题:

  • A 同学手动加了字段,B 同学本地数据库没有;
  • 测试库和生产库结构不一致;
  • 不知道某次发布改了哪些表;
  • 回滚版本时不知道数据库怎么处理;
  • 多人同时改模型,SQL 文件顺序混乱。

Django Migration 把数据库结构变化变成代码仓库里的文件,让结构变更可以被 review、提交、部署和追踪。

二、makemigrations 和 migrate 的区别

makemigrations 是“生成计划”:

python manage.py makemigrations

它比较模型定义和已有迁移状态,生成新的迁移文件,例如:

app/migrations/0002_article_status.py

migrate 是“执行计划”:

python manage.py migrate

它把未执行的迁移应用到数据库,并在 django_migrations 表里记录执行历史。

面试中很多人会混淆这两个命令。准确说:makemigrations 不一定改数据库,migrate 才真正应用到数据库。

三、迁移文件里有什么

迁移文件通常包含依赖和操作:

class Migration(migrations.Migration):
    dependencies = [
        ("blog", "0001_initial"),
    ]

    operations = [
        migrations.AddField(
            model_name="article",
            name="status",
            field=models.CharField(max_length=20, default="draft"),
        ),
    ]

它描述“在 Article 上增加 status 字段”。Django 根据数据库后端生成对应 SQL。

四、数据迁移和结构迁移要分清

结构迁移改表结构,数据迁移改已有数据。数据迁移可以用 RunPython

def fill_status(apps, schema_editor):
    Article = apps.get_model("blog", "Article")
    Article.objects.filter(status="").update(status="draft")

operations = [
    migrations.RunPython(fill_status),
]

注意这里应使用 apps.get_model() 获取历史模型,而不是直接 import 当前 models.py。因为迁移运行时要基于当时的模型状态,直接 import 当前模型可能和旧迁移不匹配。

五、线上迁移的风险

小表迁移通常没问题,大表迁移要非常慎重。例如:

  • 给大表加非空字段并带默认值;
  • 给大表建索引;
  • 改字段类型;
  • 删除字段;
  • 重命名字段;
  • 大批量数据更新。

这些操作可能锁表、拖慢数据库、导致发布窗口过长。更稳妥的做法是兼容式发布:

  1. 先加可空字段;
  2. 发布兼容新旧字段的代码;
  3. 后台分批回填数据;
  4. 再加约束或切读写;
  5. 最后清理旧字段。

六、团队协作注意事项

  • 迁移文件要提交到 Git;
  • 多人同时生成迁移后,要检查依赖冲突;
  • 已上线执行过的迁移不要直接修改;
  • 可以用 showmigrations 查看执行状态;
  • 可以用 sqlmigrate 预览 SQL;
  • 发布前在测试环境演练迁移。

七、线上迁移为什么要谨慎

Migration 在线上最怕两类问题:锁表和数据不兼容。比如给大表新增非空字段并设置默认值,在某些数据库和版本下可能触发表重写或长时间锁表;修改字段类型、删除字段、重命名字段也可能影响正在运行的旧代码。迁移不是简单执行 python manage.py migrate,而是一次数据库结构发布。

比较稳妥的做法是把危险变更拆成多步。例如新增字段时先允许为空并上线代码写入新字段,后台补齐历史数据,再改成非空约束;删除字段时先让代码不再读取该字段,再执行删除迁移。这样可以降低代码版本和数据库结构短暂不一致带来的风险。

数据迁移也要注意批量规模。RunPython 如果一次性加载所有行再更新,可能造成内存暴涨或长事务。更好的方式是分批处理、使用 iterator()、批量更新,必要时把大数据修复任务放到独立脚本或后台任务中,而不是放在一次迁移里长时间执行。

八、团队协作中的 Migration 冲突

多人同时开发时,两个分支可能都基于同一个 migration 新增了 0005_xxx.py,合并后会出现迁移分叉。Django 可以通过 makemigrations --merge 生成 merge migration,但前提是你要理解两条迁移之间是否有依赖冲突,而不是机械合并。

还有一个常见问题是随意修改已经发布到线上并执行过的 migration 文件。原则上,已经被共享环境执行过的 migration 应该视为历史记录,不要随便改内容;需要变更就新增 migration。否则不同环境的迁移状态可能不一致,后续排查会很痛苦。

面试时可以主动说:Migration 既是 schema 变更工具,也是团队数据库演进历史。开发环境可以重置,生产环境要可追溯、可回滚、可灰度。这个层次比单纯说“makemigrations 生成迁移,migrate 执行迁移”更完整。

九、常见误区与追问

命令/概念作用面试风险点
makemigrations根据模型变化生成迁移文件生成不等于已修改数据库
migrate按依赖执行迁移线上可能锁表或长事务
RunPython执行数据迁移大表要分批,避免一次性加载
  • 误区:makemigrations 会直接改数据库。 它只生成 migration 文件;真正执行结构变更的是 migrate
  • 误区:迁移文件可以随便改。 已经被共享环境或线上执行过的迁移应视为历史记录,变更应新增 migration。
  • 误区:线上迁移就是发布时跑 migrate。 线上要考虑备份、锁表、兼容发布、回滚预案和数据量。
  • 追问:大表加非空字段怎么更稳? 先加可空字段,上线兼容代码,分批回填,再加非空约束,最后清理旧逻辑。
  • 追问:多人同时生成 0005 迁移怎么办? 检查依赖和操作是否冲突,必要时用 makemigrations --merge 生成合并迁移,而不是机械改文件名。
  • 追问:数据迁移为什么不能随便写大循环? 大循环可能造成长事务、锁等待和内存压力,应该分批、限制范围,或拆到后台任务。

记忆钩子:Migration 是数据库结构的版本历史,不只是命令。开发看依赖,线上看风险,大表看锁和分批。

十、加强记忆

Django Migration 是数据库结构变更的版本控制:makemigrations 生成迁移文件,migrate 执行迁移。线上重点不是会敲命令,而是懂风险:备份、预览 SQL、大表锁表、兼容发布、数据迁移拆分、已上线迁移不乱改。