正向工程建表需避免全库重刷:禁用managed=False模型参与迁移,按模块分目录管理migrations,人工审查SQL,禁跨模块外键依赖,适配MySQL/PG索引差异,迁移脚本须含执行预估与回滚语句。
正向工程建表时,如何避免模型变更引发全库重刷
直接改 models.py 然后 makemigrations 再 migrate,在小项目里行得通,在大型数据库里等于埋雷。核心问题不是“能不能生成 sql”,而是“生成的 sql 是否可中断、可验证、可回滚”。
- 必须启用
managed = False的只读模型仅用于查询,绝不参与迁移;真实建表模型要明确归属模块,比如加前缀auth_user、billing_invoice - 迁移文件必须人工审查:用
python manage.py makemigrations --dry-run --verbosity=2看实际执行语句,重点检查是否含ALTER TABLE ... RENAME TO或隐式DROP COLUMN - 禁止跨模块字段依赖——比如
order模块的user_id外键指向auth.User,但不要指向另一个业务模块的customer.Profile,否则切分时外键约束会卡死
Django 中按模块切分 migrations 目录结构怎么设才不翻车
默认所有迁移塞进 migrations/ 一个目录,到 200+ 个迁移文件后,makemigrations 会变慢,更致命的是 migrate 执行顺序错乱导致外键找不到表。
- 每个 Django app 必须对应独立数据库 schema(通过
db_table显式指定前缀,如'auth_user'),且Meta.app_label与 app 名一致,不能靠swappable混用 - 迁移目录改造成
migrations/2024_05_billing/、migrations/2024_06_analytics/这种时间+模块命名,再配合python manage.py migrate --plan验证执行路径 - 跨模块新增外键时,必须在依赖方迁移中显式声明
dependencies = [('billing', '0012_auto_20240515_1030')],不能靠名字自动推导
MySQL / PostgreSQL 上,正向工程对索引和约束的实际影响差异
同一个 models.CharField(max_length=255, db_index=True),在 MySQL 和 PostgreSQL 上触发的底层操作完全不同,直接影响上线窗口和锁表现。
- MySQL 5.7+ 对
ADD INDEX默认阻塞写入,哪怕只是加个普通索引;PostgreSQL 12+ 支持CONCURRENTLY,但 Django 迁移不自动加这个关键字,得手动写RunSQL -
unique_together在 PostgreSQL 会生成UNIQUE INDEX,在 MySQL 可能退化为UNIQUE KEY,如果字段含NULL,行为不一致——MySQL 认为多个NULL不冲突,PostgreSQL 认为冲突 - 外键级联(
on_delete=models.CASCADE)在大表上执行DELETE时,PostgreSQL 会锁主表+从表整行,MySQL InnoDB 锁粒度更细但容易死锁,上线前必须在影子库压测
如何让 DBA 接受你的正向工程输出,而不是直接拒掉 PR
DBA 不反感自动化,反感不可控的 DDL。他们真正要的是:可预测的执行耗时、无隐式锁升级、失败后能秒级回退。
- 所有迁移脚本开头加注释块,明确写出「预计执行时间」「影响行数估算」「是否需停写」「回滚语句」,例如:
# DURATION: ~42s (based on 12M rows)<br># ROLLBACK: ALTER TABLE billing_invoice DROP COLUMN IF EXISTS is_archived;
- 禁用
django.db.migrations.operations.RunPython做数据迁移——DBA 看不见 Python 逻辑,无法审计;改用RunSQL并把 SQL 提前给 DBA 过一遍 - 上线前必须提供「迁移影响报告」:用
python manage.py sqlmigrate billing 0023_add_status输出原始 SQL,再用EXPLAIN ANALYZE(PG)或EXPLAIN FORMAT=TREE(MySQL 8.0+)跑一遍,附上执行计划截图
模块切分不是为了拆代码,是为了让每次 migrate 都落在 DBA 能预判的边界内。最常被跳过的一步是:没在测试库用真实数据量跑一遍迁移,结果上线才发现加个索引锁表 17 分钟。

















