SQL Migrations 是一个围绕数据库迁移(Database Migration)的技能,面向使用 Prisma、Drizzle、Knex、TypeORM、Alembic、Flyway 和 golang-migrate 等工具的开发者。其目标是帮助用户创建、应用、回滚和审查数据库结构变更,并在生产环境中尽量做到安全、可逆、零停机。
迁移基础
迁移是对数据库模式(schema)的版本化、增量变更。迁移按顺序执行,并通过元数据表(如 schema_migrations、_prisma_migrations)记录哪些已应用。
Up/Down:up 迁移应用变更,down 迁移撤销变更。并非所有变更都可逆——删除带数据的列会永久丢失数据。
版本化与可重复迁移:版本化迁移按顺序仅运行一次(001、002……)。可重复迁移(Flyway 的 R__ 前缀)在校验和变化时重新运行,适用于视图、函数和存储过程。
幂等迁移:使用 IF NOT EXISTS / IF EXISTS 守卫,使迁移可安全重复运行。资料给出了 CREATE TABLE IF NOT EXISTS、ALTER TABLE ... ADD COLUMN IF NOT EXISTS、DROP INDEX IF EXISTS 等示例。
工具专属模式
资料列出了各工具的安装与常用命令:
Prisma:npm install prisma @prisma/client;npx prisma init。命令包括 migrate dev、migrate deploy、migrate reset、migrate status、generate、db push、db seed、studio。
Drizzle:npm install drizzle-orm drizzle-kit。命令包括 drizzle-kit generate、migrate、push、studio、drop。
Knex:npm install knex pg;npx knex init。命令包括 migrate:make、migrate:latest、migrate:rollback、migrate:rollback --all、seed:make、seed:run。
TypeORM:migration:create、migration:generate、migration:run、migration:revert。
Alembic:alembic init、revision --autogenerate、upgrade head、downgrade -1、history、current。
Flyway:migrate、info、validate、repair、clean;命名示例 V1__Create_users.sql、V2__Add_email_index.sql,可重复迁移 R__Create_views.sql。
golang-migrate:migrate create、up、down 1、force 3。
编写安全迁移(零停机)
加列:绝不在已有数据的表上添加无默认值的 NOT NULL 列。安全顺序为:先加可空列,部署写入新列的代码,回填现有行,再加约束。
删列:绝不删除代码仍在读取的列。安全顺序为:停止读取并部署,停止写入并部署,最后删列。
重命名列或表:重命名会立即破坏现有查询。安全顺序为:加新列并部署双写,从旧列回填新列,切换读取并部署,停止写旧列并部署,删除旧列。表可沿用相同的 expand-migrate-contract 模式,也可在过渡期创建使用旧名的视图。
索引创建:在 Postgres 上,CREATE INDEX 会锁表写入,应使用 CONCURRENTLY(该方式不能在事务内运行)。在将每个文件包在事务中的工具里,需为该迁移禁用事务或单独执行索引创建。
添加约束:Postgres 可用 NOT VALID 加约束再 VALIDATE CONSTRAINT,避免长时间锁表;资料给出外键和检查约束的示例。
数据迁移与模式迁移
应将二者分离。模式迁移改变结构(DDL),数据迁移改变内容(DML)。混在一起会带来问题:模式迁移应快速可逆,而大表数据迁移很慢;模式回滚无法恢复已删除的数据;数据迁移可能需要分批而模式迁移不需要。资料给出 005_add_status_column.sql 与 006_backfill_status.sql 分离的示例。
大表迁移
对百万行级别的表,单次 ALTER TABLE 或 UPDATE 可能锁表或运行数小时。分批更新可按块处理,避免长锁和事务日志膨胀,资料给出以 10,000 为批次的 PL/pgSQL 循环示例。
MySQL 在线模式变更工具:pt-online-schema-change(Percona)与 gh-ost(GitHub)会创建影子表、复制数据、重放 binlog 变更再切换,资料给出两者的命令示例。
迁移压缩 / 合并
当迁移文件过多(100+)时可压缩:用 pg_dump --schema-only 导出当前模式作为基线,删除既有迁移文件,从导出创建单个基线迁移,并在迁移表中标记为已应用而不实际运行,后续迁移从该基线构建。Prisma 可用 npx prisma migrate diff;Drizzle 和 Knex 没有内置压缩,需手动完成。
回滚策略与局限
应始终编写 down 迁移,但要理解其限制:删除列不可逆(数据已丢失),down 迁移只能重建列而无法恢复数据;数据迁移在旧值被覆盖后无法有意义地反转;生产回滚有风险,更推荐前向修复,即部署一个新迁移来撤销变更。资料给出 down 迁移示例。
迁移锁与并发部署
多数迁移工具使用咨询锁防止两个进程同时运行迁移。Prisma 和 Flyway 会自动获取锁;Knex、golang-migrate 只应让一个实例运行迁移(用部署步骤而非应用启动时执行)。若迁移中途崩溃留下锁,可能需要手动清理锁,或使用 golang-migrate 的 migrate force / flyway repair。建议在专用 CI/CD 步骤中运行迁移,而非应用启动时。
测试迁移
应在类生产数据集上测试,而不是空数据库。执行 up→down→up 验证可逆性。在预发布环境使用匿名化的生产数据副本。检查迁移速度:若在预发布环境耗时超过 1 秒,生产会更久。
种子数据
种子用于填充初始或测试数据,应保持幂等。资料给出使用 ON CONFLICT DO NOTHING 的 SQL 种子示例,以及 Prisma 基于 upsert 的 seed.ts 示例。
环境专属迁移
开发环境:可自由使用 migrate reset / db push,速度优先于安全。预发布:镜像生产,运行完全相同的迁移文件,并用真实数据量测试。生产:绝不使用 reset、push 或 clean,只使用 migrate deploy / migrate up,迁移前务必备份。用环境变量控制连接字符串,绝不在迁移文件中硬编码凭据。
CI/CD 集成
资料给出 GitHub Actions 示例,在生产部署中使用 npx prisma migrate deploy 并通过 secrets 传入 DATABASE_URL,以及在 PR 检查中执行 migrate status 和 migrate diff --exit-code。CI 检查清单包括:合并前对测试数据库运行迁移;运行后确认没有待应用的迁移;迁移后运行应用测试套件;生产部署时,若迁移向后兼容则先迁移再部署新代码,若旧代码必须先停止使用被删除的列则反向顺序。
原生 SQL 迁移结构
资料给出 migrations/ 目录示例,包含 001_create_users.up.sql / 001_create_users.down.sql 等成对文件,并给出创建 users 表的 up 迁移与 DROP TABLE IF EXISTS 的 down 迁移示例。
快速参考
- 每个迁移只做一项变更;2. 始终编写 down 迁移;3. 绝不编辑已应用的迁移,应新建一个;4. 用类生产数据测试迁移;5. 多语句迁移包在事务中(CONCURRENTLY 除外);6. 模式迁移与数据迁移分离;7. 回填数据与模式变更分开;8. 在 CI/CD 中运行迁移而非应用启动时;9. 生产迁移前备份数据库。
适用场景与限制
该技能适用于需要创建、更新、审查数据库迁移,或在多种迁移工具间进行选择与切换,以及希望在生产环境安全变更表结构的开发者。关于价格、授权方式、支持平台与具体版本要求,资料中暂未提供。