affaan-m/ECC

database-migrations

数据库迁移最佳实践,涵盖模式变更、数据迁移、回滚以及零停机部署,适用于PostgreSQL、MySQL及常用ORM(Prisma、Drizzle、Django、TypeORM、golang-migrate)。

71CollectingRuns scripts
See how to use itView GitHub source
npx skills add https://github.com/affaan-m/ECC --skill "docs/zh-CN/skills/database-migrations"
Automated source guide

Source checked Jul 28, 2026·Refresh due Oct 26, 2026

Reorganized from the pinned upstream SKILL.md

Turn database-migrations's source instructions into a guide you can follow

According to the pinned SKILL.md from affaan-m/ECC: 数据库迁移最佳实践,涵盖模式变更、数据迁移、回滚以及零停机部署,适用于PostgreSQL、MySQL及常用ORM(Prisma、Drizzle、Django、TypeORM、golang-migrate)。

npx skills add https://github.com/affaan-m/ECC --skill "docs/zh-CN/skills/database-migrations"
Check the pinned source

Best fit

  • 数据库迁移最佳实践,涵盖模式变更、数据迁移、回滚以及零停机部署,适用于PostgreSQL、MySQL及常用ORM(Prisma、Drizzle、Django、TypeORM、golang-migrate)。

Bring this context

  • A concrete task that matches the documented purpose of database-migrations.
  • The files, examples, or context the task depends on.
  • Your constraints, target environment, and definition of done.

Expected outputs

  • A result that follows the pinned database-migrations instructions.
  • A concise record of assumptions, inputs used, and unresolved questions.
  • A final check against the source workflow and relevant permission signals.

Key source sections

Read database-migrations through these 5 source sections

Sections are extracted automatically from the pinned SKILL.md and link back to the source.

01

何时激活

创建或修改数据库表 添加/删除列或索引 运行数据迁移(回填、转换) 计划零停机模式变更 为新项目设置迁移工具

SKILL.md · 何时激活
创建或修改数据库表添加/删除列或索引运行数据迁移(回填、转换)
02

核心原则

1. 每个变更都是一次迁移 — 切勿手动更改生产数据库 2. 迁移在生产环境中是只进不退的 — 回滚使用新的前向迁移 3. 模式迁移和数据迁移是分开的 — 切勿在一个迁移中混合 DDL 和 DML 4. 针对生产规模的数据测试迁移 — 适用于 100 行的迁移可能在 1000 万行时锁定 5. 迁移一旦部署就是不可变的 — 切勿编辑已在生产中运行的迁移

SKILL.md · 核心原则
每个变更都是一次迁移 — 切勿手动更改生产数据库迁移在生产环境中是只进不退的 — 回滚使用新的前向迁移模式迁移和数据迁移是分开的 — 切勿在一个迁移中混合 DDL 和 DML
03

迁移安全检查清单

\[ ] 迁移同时包含 UP 和 DOWN(或明确标记为不可逆) \[ ] 对大表没有全表锁(使用并发操作) \[ ] 新列有默认值或可为空(切勿添加没有默认值的 NOT NULL) \[ ] 索引是并发创建的(对于现有表,不与 CREATE TABLE 内联创建) \[ ] 数据回填是与模式变更分开的迁移 \[ ] 已针对生产数据副本进行测试 \[ ] 回滚计划已记录

SKILL.md · 迁移安全检查清单
\[ ] 迁移同时包含 UP 和 DOWN(或明确标记为不可逆)\[ ] 对大表没有全表锁(使用并发操作)\[ ] 新列有默认值或可为空(切勿添加没有默认值的 NOT NULL)
04

PostgreSQL 模式

Review the “PostgreSQL 模式” section in the pinned source before continuing.

SKILL.md · PostgreSQL 模式
Review and apply the “PostgreSQL 模式” source section.
05

安全地添加列

Review the “安全地添加列” section in the pinned source before continuing.

SKILL.md · 安全地添加列
Review and apply the “安全地添加列” source section.

SkillSignal prompt templates

Provide the task, context, and acceptance criteria

These prompts were written by SkillSignal from the source structure; they are not upstream text.

Task-start prompt

Confirm source fit, inputs, and outputs before acting.

Use database-migrations to help me with: [specific task]. Context: [files, data, or background]. Constraints: [environment, scope, and prohibited actions]. Before acting, check the pinned SKILL.md and explain which sections apply, what inputs are still missing, and what you will deliver.

Source-guided execution

Make the Agent explicitly follow the key extracted sections.

Apply the pinned database-migrations source to [task]. Pay particular attention to these source sections: “何时激活”, “核心原则”, “迁移安全检查清单”, “PostgreSQL 模式”, “安全地添加列”. Preserve the important decision at each step. Mark facts not covered by the source as “needs confirmation” instead of inventing them. Then verify the result against my acceptance criteria: [criteria].

Result-review prompt

Check omissions, permissions, and source drift before delivery.

Review the current database-migrations result: (1) does it satisfy the original task; (2) were any applicable steps or limits in the pinned SKILL.md missed; (3) did it perform any unauthorized file, command, network, or data action; and (4) which conclusions remain unverified? List issues first, then fix only what the source or user authorization supports.

Output checklist

Verify each item before delivery

The task matches the purpose documented in the SKILL.md.

The source section “何时激活” has been checked.

The source section “核心原则” has been checked.

The source section “迁移安全检查清单” has been checked.

The source section “PostgreSQL 模式” has been checked.

Inputs, constraints, and acceptance criteria are explicit.

Unverified facts, compatibility, and outcome claims are clearly marked.

Any file, command, network, or data action has been reviewed.

Choose a different workflow

When another Skill is the better fit

FAQ

What does database-migrations do?

数据库迁移最佳实践,涵盖模式变更、数据迁移、回滚以及零停机部署,适用于PostgreSQL、MySQL及常用ORM(Prisma、Drizzle、Django、TypeORM、golang-migrate)。

How do I start using database-migrations?

The catalog detected this source-specific install command: npx skills add https://github.com/affaan-m/ECC --skill "docs/zh-CN/skills/database-migrations". Inspect the command and pinned source before running it.

Which Agent platforms does it declare?

No dedicated Agent platform is declared in the pinned source record.

Repository stars
234,327
Repository forks
35,711
Quality
71/100
Source repository last pushed

Quality breakdown

Based on traceable docs and repository signals; stars are not treated as quality.

71/100
Documentation25/30
Specificity11/25
Maintenance20/20
Trust signals15/25

Compare before choosing

Related Agent Skills and source variants

These links are selected from shared tasks, functions, stacks, platforms, and same-name variants. Compare the source owner, documentation, permissions, and maintenance signals.

View original Skill.mdThis page is parsed directly from the repository SKILL.md without editorial rewriting. Collected: Jul 28, 2026 · about 1 min

数据库迁移模式

为生产系统提供安全、可逆的数据库模式变更。

何时激活

  • 创建或修改数据库表
  • 添加/删除列或索引
  • 运行数据迁移(回填、转换)
  • 计划零停机模式变更
  • 为新项目设置迁移工具

核心原则

  1. 每个变更都是一次迁移 — 切勿手动更改生产数据库
  2. 迁移在生产环境中是只进不退的 — 回滚使用新的前向迁移
  3. 模式迁移和数据迁移是分开的 — 切勿在一个迁移中混合 DDL 和 DML
  4. 针对生产规模的数据测试迁移 — 适用于 100 行的迁移可能在 1000 万行时锁定
  5. 迁移一旦部署就是不可变的 — 切勿编辑已在生产中运行的迁移

迁移安全检查清单

应用任何迁移之前:

  • [ ] 迁移同时包含 UP 和 DOWN(或明确标记为不可逆)
  • [ ] 对大表没有全表锁(使用并发操作)
  • [ ] 新列有默认值或可为空(切勿添加没有默认值的 NOT NULL)
  • [ ] 索引是并发创建的(对于现有表,不与 CREATE TABLE 内联创建)
  • [ ] 数据回填是与模式变更分开的迁移
  • [ ] 已针对生产数据副本进行测试
  • [ ] 回滚计划已记录

PostgreSQL 模式

安全地添加列

-- GOOD: Nullable column, no lock
ALTER TABLE users ADD COLUMN avatar_url TEXT;

-- GOOD: Column with default (Postgres 11+ is instant, no rewrite)
ALTER TABLE users ADD COLUMN is_active BOOLEAN NOT NULL DEFAULT true;

-- BAD: NOT NULL without default on existing table (requires full rewrite)
ALTER TABLE users ADD COLUMN role TEXT NOT NULL;
-- This locks the table and rewrites every row

无停机添加索引

-- BAD: Blocks writes on large tables
CREATE INDEX idx_users_email ON users (email);

-- GOOD: Non-blocking, allows concurrent writes
CREATE INDEX CONCURRENTLY idx_users_email ON users (email);

-- Note: CONCURRENTLY cannot run inside a transaction block
-- Most migration tools need special handling for this

重命名列(零停机)

切勿在生产中直接重命名。使用扩展-收缩模式:

-- Step 1: Add new column (migration 001)
ALTER TABLE users ADD COLUMN display_name TEXT;

-- Step 2: Backfill data (migration 002, data migration)
UPDATE users SET display_name = username WHERE display_name IS NULL;

-- Step 3: Update application code to read/write both columns
-- Deploy application changes

-- Step 4: Stop writing to old column, drop it (migration 003)
ALTER TABLE users DROP COLUMN username;

安全地删除列

-- Step 1: Remove all application references to the column
-- Step 2: Deploy application without the column reference
-- Step 3: Drop column in next migration
ALTER TABLE orders DROP COLUMN legacy_status;

-- For Django: use SeparateDatabaseAndState to remove from model
-- without generating DROP COLUMN (then drop in next migration)

大型数据迁移

-- BAD: Updates all rows in one transaction (locks table)
UPDATE users SET normalized_email = LOWER(email);

-- GOOD: Batch update with progress
DO $$
DECLARE
  batch_size INT := 10000;
  rows_updated INT;
BEGIN
  LOOP
    UPDATE users
    SET normalized_email = LOWER(email)
    WHERE id IN (
      SELECT id FROM users
      WHERE normalized_email IS NULL
      LIMIT batch_size
      FOR UPDATE SKIP LOCKED
    );
    GET DIAGNOSTICS rows_updated = ROW_COUNT;
    RAISE NOTICE 'Updated % rows', rows_updated;
    EXIT WHEN rows_updated = 0;
    COMMIT;
  END LOOP;
END $$;

Prisma (TypeScript/Node.js)

工作流

# Create migration from schema changes
npx prisma migrate dev --name add_user_avatar

# Apply pending migrations in production
npx prisma migrate deploy

# Reset database (dev only)
npx prisma migrate reset

# Generate client after schema changes
npx prisma generate

模式示例

model User {
  id        String   @id @default(cuid())
  email     String   @unique
  name      String?
  avatarUrl String?  @map("avatar_url")
  createdAt DateTime @default(now()) @map("created_at")
  updatedAt DateTime @updatedAt @map("updated_at")
  orders    Order[]

  @@map("users")
  @@index([email])
}

自定义 SQL 迁移

对于 Prisma 无法表达的操作(并发索引、数据回填):

# Create empty migration, then edit the SQL manually
npx prisma migrate dev --create-only --name add_email_index
-- migrations/20240115_add_email_index/migration.sql
-- Prisma cannot generate CONCURRENTLY, so we write it manually
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_users_email ON users (email);

Drizzle (TypeScript/Node.js)

工作流

# Generate migration from schema changes
npx drizzle-kit generate

# Apply migrations
npx drizzle-kit migrate

# Push schema directly (dev only, no migration file)
npx drizzle-kit push

模式示例

import { pgTable, text, timestamp, uuid, boolean } from "drizzle-orm/pg-core";

export const users = pgTable("users", {
  id: uuid("id").primaryKey().defaultRandom(),
  email: text("email").notNull().unique(),
  name: text("name"),
  isActive: boolean("is_active").notNull().default(true),
  createdAt: timestamp("created_at").notNull().defaultNow(),
  updatedAt: timestamp("updated_at").notNull().defaultNow(),
});

Django (Python)

工作流

# Generate migration from model changes
python manage.py makemigrations

# Apply migrations
python manage.py migrate

# Show migration status
python manage.py showmigrations

# Generate empty migration for custom SQL
python manage.py makemigrations --empty app_name -n description

数据迁移

from django.db import migrations

def backfill_display_names(apps, schema_editor):
    User = apps.get_model("accounts", "User")
    batch_size = 5000
    users = User.objects.filter(display_name="")
    while users.exists():
        batch = list(users[:batch_size])
        for user in batch:
            user.display_name = user.username
        User.objects.bulk_update(batch, ["display_name"], batch_size=batch_size)

def reverse_backfill(apps, schema_editor):
    pass  # Data migration, no reverse needed

class Migration(migrations.Migration):
    dependencies = [("accounts", "0015_add_display_name")]

    operations = [
        migrations.RunPython(backfill_display_names, reverse_backfill),
    ]

SeparateDatabaseAndState

从 Django 模型中删除列,而不立即从数据库中删除:

class Migration(migrations.Migration):
    operations = [
        migrations.SeparateDatabaseAndState(
            state_operations=[
                migrations.RemoveField(model_name="user", name="legacy_field"),
            ],
            database_operations=[],  # Don't touch the DB yet
        ),
    ]

golang-migrate (Go)

工作流

# Create migration pair
migrate create -ext sql -dir migrations -seq add_user_avatar

# Apply all pending migrations
migrate -path migrations -database "$DATABASE_URL" up

# Rollback last migration
migrate -path migrations -database "$DATABASE_URL" down 1

# Force version (fix dirty state)
migrate -path migrations -database "$DATABASE_URL" force VERSION

迁移文件

-- migrations/000003_add_user_avatar.up.sql
ALTER TABLE users ADD COLUMN avatar_url TEXT;
CREATE INDEX CONCURRENTLY idx_users_avatar ON users (avatar_url) WHERE avatar_url IS NOT NULL;

-- migrations/000003_add_user_avatar.down.sql
DROP INDEX IF EXISTS idx_users_avatar;
ALTER TABLE users DROP COLUMN IF EXISTS avatar_url;

零停机迁移策略

对于关键的生产变更,遵循扩展-收缩模式:

Phase 1: EXPAND
  - 添加新列/表(可为空或带有默认值)
  - 部署:应用同时写入旧数据和新数据
  - 回填现有数据

Phase 2: MIGRATE
  - 部署:应用读取新数据,同时写入新旧数据
  - 验证数据一致性

Phase 3: CONTRACT
  - 部署:应用仅使用新数据
  - 在单独迁移中删除旧列/表

时间线示例

Day 1:迁移添加新的 `new_status` 列(可空)
Day 1:部署应用 v2 —— 同时写入 `status` 和 `new_status`
Day 2:运行针对现有行的回填迁移
Day 3:部署应用 v3 —— 仅从 `new_status` 读取
Day 7:迁移删除旧的 `status` 列

反模式

反模式为何会失败更好的方法
在生产中手动执行 SQL没有审计追踪,不可重复始终使用迁移文件
编辑已部署的迁移导致环境间出现差异改为创建新迁移
没有默认值的 NOT NULL锁定表,重写所有行添加可为空列,回填数据,然后添加约束
在大表上内联创建索引在构建期间阻塞写入使用 CREATE INDEX CONCURRENTLY
在一个迁移中混合模式和数据的变更难以回滚,事务时间长分开的迁移
在移除代码之前删除列应用程序在缺失列时出错先移除代码,下一次部署再删除列
Source repo
affaan-m/ECC
Skill path
docs/zh-CN/skills/database-migrations/SKILL.md
Commit SHA
4e973d3eaf92
Repository license
MIT
Data collected