跳到正文
cccskills
免费在 GitHub 公开中文介绍

database-migrations

用于规划生产数据库的结构变更和现有数据迁移,整理列与索引的添加、分阶段切换及恢复步骤,帮助在变更时考虑停机时间和数据库锁。

原文查看原文说明

Buenas prácticas de migración de base de datos para cambios de esquema, migraciones de datos, rollbacks y despliegues de tiempo cero en PostgreSQL, MySQL y ORMs comunes (Prisma, Drizzle, Kysely, Django, TypeORM, golang-migrate).

集成工具PostgreSQLMySQL
AI 评分90分/ 100查看安装方法

适用场景

  • 为生产数据表添加列或索引
  • 分批更新大量数据
  • 在不中断应用的情况下替换列
  • 为新项目配置迁移工具

中文介绍

功能

整理修改生产数据库结构和现有数据的迁移步骤,涵盖添加或删除列与索引、补全现有行的值、大量数据的分批更新,以及撤销变更的计划。还介绍先同时使用新旧列再切换的分阶段迁移方式,并提供 Prisma、Drizzle、Kysely、Django 和 golang-migrate 的操作示例。

适用场景

适合为运行中的应用添加字段,或修改列名、数据格式时使用。当大表变更可能导致长时间暂停,或需要为新项目建立迁移文件管理流程时,也可参考。

使用示例

  • “规划为生产环境 users 表添加列的迁移和恢复步骤。”
  • “将现有数据的补全与结构变更拆成独立迁移。”
  • “结合应用的分阶段部署,规划列的替换过程。”

注意事项

需要根据数据库和迁移工具选择步骤,并使用生产规模的数据验证。已经应用的迁移文件不应编辑,应通过新迁移修正。PostgreSQL 的 CREATE INDEX CONCURRENTLY 不能在事务中执行。数据库重置和直接同步结构仅作为开发环境操作。

本介绍由 AI(OpenAI Codex)根据公开的 SKILL.md 撰写。准确的规格请查看下方原文。

客观评估

AI 仅根据 SKILL.md 的内容,从 4 个方面进行了评估。星标数和热度不计入评估。

指令清晰度能否看出何时使用、做什么
A良好明确列出列与索引变更、数据补全、不停机迁移和工具配置等使用场景。
步骤具体性步骤、输入输出和示例是否具体
A良好提供分阶段替换列的步骤、SQL示例和Prisma等工具的执行命令。
安全性发送数据、删除文件、执行命令等是否有说明
A良好要求应用前验证并制定恢复计划,且将数据库重置限定于开发环境。
安装难易度所需工具和 API 密钥是否少且有说明
B部分不足提供各工具的操作示例,但没有数据库和迁移工具的安装步骤。

总分按每项 A=25 分、B=15 分、C=5 分相加。 由 AI(OpenAI Codex)于 2026年10月19日 评估。仅供参考,安装前请同时查看原文。

如果您认为评估有误,请申请复核

请写明哪一项有误以及原因。无需登录。我们会对照 SKILL.md 重新评估,并在此处更新结果。

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

包含的文件(1)

  • SKILL.md12.9 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

Patrones de Migración de Base de Datos

Cambios de esquema de base de datos seguros y reversibles para sistemas de producción.

Cuándo Activar

  • Crear o alterar tablas de base de datos
  • Agregar/eliminar columnas o índices
  • Ejecutar migraciones de datos (backfill, transformación)
  • Planificar cambios de esquema de tiempo cero (zero-downtime)
  • Configurar herramientas de migración para un nuevo proyecto

Principios Fundamentales

  1. Cada cambio es una migración — nunca alterar bases de datos de producción manualmente
  2. Las migraciones son solo hacia adelante en producción — los rollbacks usan nuevas migraciones hacia adelante
  3. Las migraciones de esquema y de datos son separadas — nunca mezclar DDL y DML en una migración
  4. Probar migraciones contra datos de tamaño de producción — una migración que funciona en 100 filas puede bloquear en 10M
  5. Las migraciones son inmutables una vez desplegadas — nunca editar una migración que ya se ejecutó en producción

Lista de Verificación de Seguridad de Migración

Antes de aplicar cualquier migración:

  • La migración tiene tanto UP como DOWN (o está marcada explícitamente como irreversible)
  • Sin bloqueos de tabla completa en tablas grandes (usar operaciones concurrentes)
  • Las nuevas columnas tienen valores predeterminados o son nullable (nunca agregar NOT NULL sin valor predeterminado)
  • Índices creados de forma concurrente (no en línea con CREATE TABLE para tablas existentes)
  • El backfill de datos es una migración separada del cambio de esquema
  • Probado contra una copia de datos de producción
  • Plan de rollback documentado

Patrones PostgreSQL

Agregar una Columna de Forma Segura

-- BIEN: Columna nullable, sin bloqueo
ALTER TABLE users ADD COLUMN avatar_url TEXT;

-- BIEN: Columna con valor predeterminado (Postgres 11+ es instantáneo, sin reescritura)
ALTER TABLE users ADD COLUMN is_active BOOLEAN NOT NULL DEFAULT true;

-- MAL: NOT NULL sin valor predeterminado en tabla existente (requiere reescritura completa)
ALTER TABLE users ADD COLUMN role TEXT NOT NULL;
-- Esto bloquea la tabla y reescribe cada fila

Agregar un Índice Sin Tiempo de Inactividad

-- MAL: Bloquea escrituras en tablas grandes
CREATE INDEX idx_users_email ON users (email);

-- BIEN: No bloqueante, permite escrituras concurrentes
CREATE INDEX CONCURRENTLY idx_users_email ON users (email);

-- Nota: CONCURRENTLY no puede ejecutarse dentro de un bloque de transacción
-- La mayoría de herramientas de migración necesitan manejo especial para esto

Renombrar una Columna (Zero-Downtime)

Nunca renombrar directamente en producción. Usar el patrón expand-contract:

-- Paso 1: Agregar nueva columna (migración 001)
ALTER TABLE users ADD COLUMN display_name TEXT;

-- Paso 2: Backfill de datos (migración 002, migración de datos)
UPDATE users SET display_name = username WHERE display_name IS NULL;

-- Paso 3: Actualizar el código de la aplicación para leer/escribir ambas columnas
-- Desplegar cambios de aplicación

-- Paso 4: Dejar de escribir en la columna antigua, eliminarla (migración 003)
ALTER TABLE users DROP COLUMN username;

Eliminar una Columna de Forma Segura

-- Paso 1: Eliminar todas las referencias de la aplicación a la columna
-- Paso 2: Desplegar la aplicación sin la referencia a la columna
-- Paso 3: Eliminar la columna en la próxima migración
ALTER TABLE orders DROP COLUMN legacy_status;

-- Para Django: usar SeparateDatabaseAndState para eliminar del modelo
-- sin generar DROP COLUMN (luego eliminar en la próxima migración)

Migraciones de Datos Grandes

-- MAL: Actualiza todas las filas en una transacción (bloquea la tabla)
UPDATE users SET normalized_email = LOWER(email);

-- BIEN: Actualización en lotes con progreso
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)

Flujo de Trabajo

# Crear migración a partir de cambios de esquema
npx prisma migrate dev --name add_user_avatar

# Aplicar migraciones pendientes en producción
npx prisma migrate deploy

# Resetear base de datos (solo desarrollo)
npx prisma migrate reset

# Generar cliente después de cambios de esquema
npx prisma generate

Ejemplo de Esquema

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])
}

Migración SQL Personalizada

Para operaciones que Prisma no puede expresar (índices concurrentes, backfills de datos):

# Crear migración vacía, luego editar el SQL manualmente
npx prisma migrate dev --create-only --name add_email_index
-- migrations/20240115_add_email_index/migration.sql
-- Prisma no puede generar CONCURRENTLY, por lo que se escribe manualmente
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_users_email ON users (email);

Drizzle (TypeScript/Node.js)

Flujo de Trabajo

# Generar migración a partir de cambios de esquema
npx drizzle-kit generate

# Aplicar migraciones
npx drizzle-kit migrate

# Hacer push del esquema directamente (solo desarrollo, sin archivo de migración)
npx drizzle-kit push

Ejemplo de Esquema

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(),
});

Kysely (TypeScript/Node.js)

Flujo de Trabajo (kysely-ctl)

# Inicializar archivo de configuración (kysely.config.ts)
kysely init

# Crear un nuevo archivo de migración
kysely migrate make add_user_avatar

# Aplicar todas las migraciones pendientes
kysely migrate latest

# Revertir la última migración
kysely migrate down

# Mostrar estado de migraciones
kysely migrate list

Archivo de Migración

// migrations/2024_01_15_001_create_user_profile.ts
import { type Kysely, sql } from 'kysely'

// IMPORTANTE: Siempre usar Kysely<any>, no tu interfaz de DB tipada.
// Las migraciones están congeladas en el tiempo y no deben depender de los tipos de esquema actuales.
export async function up(db: Kysely<any>): Promise<void> {
  await db.schema
    .createTable('user_profile')
    .addColumn('id', 'serial', (col) => col.primaryKey())
    .addColumn('email', 'varchar(255)', (col) => col.notNull().unique())
    .addColumn('avatar_url', 'text')
    .addColumn('created_at', 'timestamp', (col) =>
      col.defaultTo(sql`now()`).notNull()
    )
    .execute()

  await db.schema
    .createIndex('idx_user_profile_avatar')
    .on('user_profile')
    .column('avatar_url')
    .execute()
}

export async function down(db: Kysely<any>): Promise<void> {
  await db.schema.dropTable('user_profile').execute()
}

Migrador Programático

import { Migrator, FileMigrationProvider } from 'kysely'
import { promises as fs } from 'fs'
import * as path from 'path'
// Solo ESM — CJS puede usar __dirname directamente
import { fileURLToPath } from 'url'
const migrationFolder = path.join(
  path.dirname(fileURLToPath(import.meta.url)),
  './migrations',
)

// `db` es tu instancia de base de datos Kysely<any>
const migrator = new Migrator({
  db,
  provider: new FileMigrationProvider({
    fs,
    path,
    migrationFolder,
  }),
  // ADVERTENCIA: Solo habilitar en desarrollo. Deshabilita la validación de
  // ordenamiento por timestamp, lo que puede causar deriva de esquema entre entornos.
  // allowUnorderedMigrations: true,
})

const { error, results } = await migrator.migrateToLatest()

results?.forEach((it) => {
  if (it.status === 'Success') {
    console.log(`migration "${it.migrationName}" executed successfully`)
  } else if (it.status === 'Error') {
    console.error(`failed to execute migration "${it.migrationName}"`)
  }
})

if (error) {
  console.error('migration failed', error)
  process.exit(1)
}

Django (Python)

Flujo de Trabajo

# Generar migración a partir de cambios de modelo
python manage.py makemigrations

# Aplicar migraciones
python manage.py migrate

# Mostrar estado de migraciones
python manage.py showmigrations

# Generar migración vacía para SQL personalizado
python manage.py makemigrations --empty app_name -n description

Migración de Datos

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  # Migración de datos, no se necesita reversión

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

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

SeparateDatabaseAndState

Eliminar una columna del modelo Django sin eliminarla de la base de datos inmediatamente:

class Migration(migrations.Migration):
    operations = [
        migrations.SeparateDatabaseAndState(
            state_operations=[
                migrations.RemoveField(model_name="user", name="legacy_field"),
            ],
            database_operations=[],  # No tocar la DB todavía
        ),
    ]

golang-migrate (Go)

Flujo de Trabajo

# Crear par de migración
migrate create -ext sql -dir migrations -seq add_user_avatar

# Aplicar todas las migraciones pendientes
migrate -path migrations -database "$DATABASE_URL" up

# Revertir la última migración
migrate -path migrations -database "$DATABASE_URL" down 1

# Forzar versión (corregir estado sucio)
migrate -path migrations -database "$DATABASE_URL" force VERSION

Archivos de Migración

-- 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;

Estrategia de Migración de Zero-Downtime

Para cambios críticos de producción, seguir el patrón expand-contract:

Fase 1: EXPAND (Expandir)
  - Agregar nueva columna/tabla (nullable o con valor predeterminado)
  - Desplegar: la app escribe en AMBAS, vieja y nueva
  - Backfill de datos existentes

Fase 2: MIGRATE (Migrar)
  - Desplegar: la app lee de la NUEVA, escribe en AMBAS
  - Verificar consistencia de datos

Fase 3: CONTRACT (Contraer)
  - Desplegar: la app solo usa la NUEVA
  - Eliminar columna/tabla antigua en migración separada

Ejemplo de Línea de Tiempo

Día 1: Migración agrega columna new_status (nullable)
Día 1: Desplegar app v2 — escribe en status y new_status
Día 2: Ejecutar migración de backfill para filas existentes
Día 3: Desplegar app v3 — lee solo de new_status
Día 7: Migración elimina columna status antigua

Anti-Patrones

Anti-PatrónPor Qué FallaMejor Enfoque
SQL manual en producciónSin historial de auditoría, no repetibleSiempre usar archivos de migración
Editar migraciones desplegadasCausa deriva entre entornosCrear nueva migración en su lugar
NOT NULL sin valor predeterminadoBloquea tabla, reescribe todas las filasAgregar nullable, backfill, luego agregar restricción
Índice en línea en tabla grandeBloquea escrituras durante la construcciónCREATE INDEX CONCURRENTLY
Esquema + datos en una migraciónDifícil de revertir, transacciones largasMigraciones separadas
Eliminar columna antes de eliminar códigoErrores de aplicación por columna faltanteEliminar código primero, eliminar columna en el próximo despliegue

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

accessibility

免费有简介AI 评分 100分

依据WCAG 2.2设计和检查Web、iOS及Android界面,核查读屏标签、键盘操作、配色对比度和操作目标尺寸,使界面支持辅助技术。

  • 为纯图标按钮添加说明
  • 检查键盘操作与模态框焦点
  • 检查对比度与操作目标尺寸
affaan-m/ECC28.4万2026年10月16日 更新

agent-architecture-audit

免费有简介AI 评分 80分

从指令、记忆、工具执行和界面显示等十二个层面排查人工智能代理故障,依据代码和日志梳理原因,按严重程度列出问题并提出修复方案。

  • 排查仅在应用内出现的故障
  • 调查旧对话混入回答的原因
  • 核对工具漏执行或虚报执行
affaan-m/ECC28.4万2026年10月16日 更新

agent-eval

免费有简介AI 评分 80分

用实际开发任务比较多个编程智能体,记录成功率、可获取的API费用、耗时和重复运行的稳定性,为工具选型及更新后的性能评估提供数据。

  • 用实际开发任务比较智能体
  • 采用新工具或模型前进行评估
  • 检查智能体更新后的性能
affaan-m/ECC28.4万2026年10月16日 更新

agent-harness-construction

免费有简介AI 评分 100分

设计和调整AI智能体使用的工具、输入与返回格式,明确出错后的恢复、重试和停止条件,管理上下文信息量,并以完成率、重试次数和成功任务成本评估改进。

  • 设计智能体工具及输入格式
  • 明确工具结果与下一步行动
  • 制定安全重试与停止条件
affaan-m/ECC28.4万2026年10月16日 更新

agent-introspection-debugging

免费有简介AI 评分 100分

根据错误信息和执行环境,分析AI代理反复失败或重复操作的原因。通过小范围、可撤销的恢复操作检验假设,并将结果与依据整理成可供交接的报告。

  • 诊断代理连续失败
  • 调查重复调用工具的循环
  • 检查上下文积累导致的输出退化
affaan-m/ECC28.4万2026年10月5日 更新

agent-introspection-debugging

免费有简介AI 评分 100分

在AI代理反复失败或重复操作时,记录错误与环境前提、排查原因,采取影响有限的恢复操作,并汇总结果和证据,为有依据的重试与交接提供支持。

  • 排查重复调用工具的原因
  • 调查对话膨胀导致的质量下降
  • 核对文件路径与环境差异
affaan-m/ECC28.4万2026年10月16日 更新

查看 affaan-m 的全部技能

报告此技能的问题