跳到主要内容

数据库迁移与版本管理

问题

如何管理数据库 Schema 变更?如何实现零停机迁移?

答案

迁移工具对比

工具配合 ORM特点
Prisma MigratePrisma声明式,自动生成 SQL
TypeORM MigrationTypeORM代码式,手写迁移
drizzle-kitDrizzle自动检测 Schema 差异
knex migrationsKnex手写 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. 部署 1:添加新列,双写(新旧列都写)
  2. 部署 2:读从新列,迁移历史数据
  3. 部署 3:删除旧列

常见面试问题

Q1: 迁移文件要不要提交到 Git?

答案

必须提交。 迁移文件是数据库版本的历史记录,和代码一样需要版本管理。团队成员拉取代码后执行迁移即可同步数据库结构。

Q2: 如何回滚迁移?

答案

  1. 每个迁移文件都写 down 方法
  2. 生产环境尽量不回滚,而是创建新的迁移修复
  3. 回滚前确认数据兼容性

Q3: 大表加索引会锁表吗?

答案

MySQL 默认会锁表。PostgreSQL 可以用 CREATE INDEX CONCURRENTLY 不锁表。MySQL 可以用 pt-online-schema-change 工具。

相关链接