数据库迁移与版本管理
问题
如何管理数据库 Schema 变更?如何实现零停机迁移?
答案
迁移工具对比
| 工具 | 配合 ORM | 特点 |
|---|---|---|
| Prisma Migrate | Prisma | 声明式,自动生成 SQL |
| TypeORM Migration | TypeORM | 代码式,手写迁移 |
| drizzle-kit | Drizzle | 自动检测 Schema 差异 |
| knex migrations | Knex | 手写 up/down |
| golang-migrate | 无 | 纯 SQL 文件 |
Prisma 迁移流程
# 1. 修改 schema.prisma
# 2. 生成迁移文件
npx prisma migrate dev --name add_user_role
# 3. 生产环境应用迁移
npx prisma migrate deploy
schema.prisma
model User {
id String @id @default(uuid())
name String
email String @unique
role Role @default(USER) // 新增字段
}
enum Role {
USER
ADMIN
}
手写迁移(TypeORM)
migration.ts
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddUserRole1700000000000 implements MigrationInterface {
async up(queryRunner: QueryRunner): Promise<void> {
// 1. 添加列(允许 NULL,不影响现有数据)
await queryRunner.query(
`ALTER TABLE users ADD COLUMN role VARCHAR(20) DEFAULT 'USER'`,
);
// 2. 回填数据
await queryRunner.query(
`UPDATE users SET role = 'ADMIN' WHERE is_admin = true`,
);
// 3. 设置 NOT NULL(数据填充完毕后)
await queryRunner.query(
`ALTER TABLE users ALTER COLUMN role SET NOT NULL`,
);
}
async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE users DROP COLUMN role`);
}
}
零停机迁移策略
危险操作
直接修改列类型、删除列、添加 NOT NULL 列(无默认值)可能导致停机。
三步走策略(重命名列为例):
- 部署 1:添加新列,双写(新旧列都写)
- 部署 2:读从新列,迁移历史数据
- 部署 3:删除旧列
常见面试问题
Q1: 迁移文件要不要提交到 Git?
答案:
必须提交。 迁移文件是数据库版本的历史记录,和代码一样需要版本管理。团队成员拉取代码后执行迁移即可同步数据库结构。
Q2: 如何回滚迁移?
答案:
- 每个迁移文件都写
down方法 - 生产环境尽量不回滚,而是创建新的迁移修复
- 回滚前确认数据兼容性
Q3: 大表加索引会锁表吗?
答案:
MySQL 默认会锁表。PostgreSQL 可以用 CREATE INDEX CONCURRENTLY 不锁表。MySQL 可以用 pt-online-schema-change 工具。