用途
该技能面向生产系统的数据库 schema 安全、可逆变更,提供一整套迁移模式与实践指南。当需要创建或修改数据库表、添加或删除列或索引、运行数据迁移(回填、转换)、规划零停机 schema 变更,或为新项目搭建迁移工具时启用。
核心原则
- 每次变更都是一次迁移——绝不在生产库手动改结构
- 生产环境迁移仅向前——回滚通过新的向前迁移实现
- schema 迁移与数据迁移分离——绝不在一个迁移中混用 DDL 与 DML
- 针对生产规模数据测试迁移——在 100 行上可行的迁移可能在 1000 万行上锁表
- 迁移一旦部署即不可变——绝不编辑已在生产运行过的迁移
迁移安全清单
应用任何迁移之前检查:迁移同时具备 UP 和 DOWN(或明确标记不可逆);大表上不使用全表锁(使用并发操作);新列有默认值或可为空(绝不添加无默认值的 NOT NULL);索引以并发方式创建(对已有表不在 CREATE TABLE 中内联创建);数据回填与 schema 变更分离为不同迁移;已针对生产数据副本测试;已记录回滚计划。
PostgreSQL 模式
安全添加列:可为空列不加锁;带默认值的列在 Postgres 11+ 为瞬时操作,不重写表;无默认值的 NOT NULL 列会锁表并重写每一行。
无停机添加索引:使用 CREATE INDEX CONCURRENTLY,非阻塞并允许并发写入;注意 CONCURRENTLY 不能在事务块内运行,多数迁移工具需要特殊处理。
零停机重命名列:绝不在生产直接重命名,采用 expand-contract 模式——先添加新列,再回填数据,然后更新应用读写两列并部署,最后停止写入旧列并删除。
安全删除列:先移除应用中对列的引用,部署不含该引用的应用,再在下一个迁移中删除列。
大批量数据迁移:避免单事务更新所有行,采用批量更新并输出进度,示例使用 FOR UPDATE SKIP LOCKED 与批次循环。
各工具工作流
Prisma:npx prisma migrate dev --name 创建迁移,npx prisma migrate deploy 生产应用,npx prisma migrate reset 开发重置,npx prisma generate 生成客户端。对 Prisma 无法表达的操作(如并发索引)使用 --create-only 手动编辑 SQL。
Drizzle:npx drizzle-kit generate 生成迁移,npx drizzle-kit migrate 应用,npx drizzle-kit push 直接推送(仅开发)。
Kysely:kysely init、kysely migrate make、kysely migrate latest、kysely migrate down、kysely migrate list。迁移文件中始终使用 Kysely<any>,并可通过 Migrator 编程式运行。
Django:makemigrations 生成、migrate 应用、showmigrations 查看状态、makemigrations --empty 生成空迁移用于自定义 SQL。使用 RunPython 执行数据迁移,SeparateDatabaseAndState 从模型移除字段而不立即删库列。
golang-migrate:migrate create 创建、migrate ... up 应用、migrate ... down 1 回滚、migrate ... force VERSION 修复脏状态。
零停机迁移策略(expand-contract)
EXPAND 阶段:添加新列/表(可空或带默认值),部署应用同时写入新旧两处,回填已有数据。MIGRATE 阶段:部署应用从新列读取、双写,验证数据一致性。CONTRACT 阶段:部署应用仅使用新列,在单独迁移中删除旧列/表。
反模式
生产手动 SQL(无审计、不可重复)应改用迁移文件;编辑已部署迁移(导致环境漂移)应新建迁移;无默认值 NOT NULL(锁表重写)应先可空、回填、再加约束;大表内联索引(阻塞写入)应使用 CONCURRENTLY;schema 与数据混在一个迁移(难回滚、长事务)应分离;先删列后删代码(应用报错)应先删代码、下次部署再删列。