关于 database-migration
面向数据库迁移任务的指导技能,涵盖 Sequelize、TypeORM 和 Prisma 等 ORM 的迁移,以及架构与数据变更、回滚和零停机部署。提供示例与验证建议;部分参考资料和脚本未随目录副本提供。
主要功能
- 01Sequelize、TypeORM、Prisma 迁移
- 02数据库架构变更指导
- 03数据转换与跨库迁移
- 04回滚与恢复策略
- 05零停机迁移建议
- 06迁移验证与最佳实践
适用场景
- 01在不同 ORM 之间迁移
- 02重构数据库架构或数据模型
- 03规划数据迁移及回滚
- 04升级数据库并减少停机
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ sickn33/ agentic-awesome-skills --skill database-migration安装后在 Claude Code 中输入/database-migration确认已加载。
详细介绍
支持材料可用性
本目录副本包含以下内嵌指南,但不包含以下支持路径中的文件。这些文件是外部前置条件,并未随附:
<support-root>/references/orm-switching.md<support-root>/references/schema-migration.md<support-root>/references/data-transformation.md<support-root>/references/rollback-strategies.md<support-root>/assets/schema-migration-template.sql<support-root>/assets/data-migration-script.py<support-root>/scripts/test-migration.sh
使用依赖这些路径的步骤前,请索取原始支持材料或现有安装;本目录未提供经验证的下载来源。请根据该安装解析每个路径,检查其内容,并核实适用的许可证和执行前置条件。所需文件不存在时,不要运行命令,也不要声称已遵循引用的流程。不要伪造缺失的脚本,也不要将名称相同但无关的文件视为等效文件。完全记录在此处的独立步骤,可在用户授权范围内继续执行;请将任何依赖项报告为不可用。
数据库迁移
掌握跨 ORM(Sequelize、TypeORM、Prisma)的数据库架构和数据迁移,包括回滚策略与零停机部署。
以下情况请勿使用此技能
- 任务与数据库迁移无关
- 需要使用本范围之外的其他领域或工具
说明
- 明确目标、约束和所需输入。
- 应用相关最佳实践并验证结果。
- 提供可执行的步骤和验证方法。
以下情况请使用此技能
- 在不同 ORM 之间迁移
- 执行架构转换
- 在数据库之间迁移数据
- 实施回滚流程
- 零停机部署
- 数据库版本升级
- 重构数据模型
ORM 迁移
Sequelize 迁移
// migrations/20231201-create-users.js
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('users', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
email: {
type: Sequelize.STRING,
unique: true,
allowNull: false
},
createdAt: Sequelize.DATE,
updatedAt: Sequelize.DATE
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('users');
}
};
// Run: npx sequelize-cli db:migrate
// Rollback: npx sequelize-cli db:migrate:undo
TypeORM 迁移
// migrations/1701234567-CreateUsers.ts
import { MigrationInterface, QueryRunner, Table } from 'typeorm';
export class CreateUsers1701234567 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.createTable(
new Table({
name: 'users',
columns: [
{
name: 'id',
type: 'int',
isPrimary: true,
isGenerated: true,
generationStrategy: 'increment'
},
{
name: 'email',
type: 'varchar',
isUnique: true
},
{
name: 'created_at',
type: 'timestamp',
default: 'CURRENT_TIMESTAMP'
}
]
})
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropTable('users');
}
}
// Run: npm run typeorm migration:run
// Rollback: npm run typeorm migration:revert
Prisma 迁移
// schema.prisma
model User {
id Int @id @default(autoincrement())
email String @unique
createdAt DateTime @default(now())
}
// Generate migration: npx prisma migrate dev --name create_users
// Apply: npx prisma migrate deploy
架构转换
添加带默认值的列
// Illustrative migration: lock/rewrite behavior depends on engine/version/table size
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.addColumn('users', 'status', {
type: Sequelize.STRING,
defaultValue: 'active',
allowNull: false
});
},
down: async (queryInterface) => {
await queryInterface.removeColumn('users', 'status');
}
};
重命名列(扩展/收缩示意)
此示意不足以应对并发写入。请先部署兼容的双写,再执行有界回填;在回填期间协调变更,并仅在验证通过后切换读取;待所有读写方完成迁移后,再删除旧列。单次 UPDATE 可能导致更新丢失。
// Step 1: Add new column
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.addColumn('users', 'full_name', {
type: Sequelize.STRING
});
// Copy data from old column
await queryInterface.sequelize.query(
'UPDATE users SET full_name = name'
);
},
down: async (queryInterface) => {
await queryInterface.removeColumn('users', 'full_name');
}
};
// Step 2: Update application to use new column
// Step 3: Remove old column
module.exports = {
up: async (queryInterface) => {
await queryInterface.removeColumn('users', 'name');
},
down: async (queryInterface, Sequelize) => {
await queryInterface.addColumn('users', 'name', {
type: Sequelize.STRING
});
}
};
更改列类型
此示例是具有破坏性且可能造成数据丢失的迁移,并非零停机方案。请先验证类型转换以及范围和空值的处理。保留原始值,直到单独获批的收缩步骤;转换回原类型无法恢复已丢弃的数据。
module.exports = {
up: async (queryInterface, Sequelize) => {
// For large tables, use multi-step approach
// 1. Add new column
await queryInterface.addColumn('users', 'age_new', {
type: Sequelize.INTEGER
});
// 2. Copy and transform data
await queryInterface.sequelize.query(`
UPDATE users
SET age_new = CAST(age AS INTEGER)
WHERE age IS NOT NULL
`);
// 3. Drop old column
await queryInterface.removeColumn('users', 'age');
// 4. Rename new column
await queryInterface.renameColumn('users', 'age_new', 'age');
},
down: async (queryInterface, Sequelize) => {
await queryInterface.changeColumn('users', 'age', {
type: Sequelize.STRING
});
}
};
数据转换
复杂数据迁移
此处仅演示解析方法:请先定义并验证地址架构;按逗号拆分会造成数据丢失。请使用有界批次并设置检查点;在完成协调且经过测试的恢复方案通过前,保留源列。以下 down() 重建操作无法恢复原始格式。
module.exports = {
up: async (queryInterface, Sequelize) => {
// Get all records
const [users] = await queryInterface.sequelize.query(
'SELECT id, address_string FROM users'
);
// Transform each record
for (const user of users) {
const addressParts = user.address_string.split(',');
await queryInterface.sequelize.query(
`UPDATE users
SET street = :street,
city = :city,
state = :state
WHERE id = :id`,
{
replacements: {
id: user.id,
street: addressParts[0]?.trim(),
city: addressParts[1]?.trim(),
state: addressParts[2]?.trim()
}
}
);
}
// Drop old column
await queryInterface.removeColumn('users', 'address_string');
},
down: async (queryInterface, Sequelize) => {
// Reconstruct original column
await queryInterface.addColumn('users', 'address_string', {
type: Sequelize.STRING
});
await queryInterface.sequelize.query(`
UPDATE users
SET address_string = CONCAT(street, ', ', city, ', ', state)
`);
await queryInterface.removeColumn('users', 'street');
await queryInterface.removeColumn('users', 'city');
await queryInterface.removeColumn('users', 'state');
}
};
回滚策略
基于事务的迁移
module.exports = {
up: async (queryInterface, Sequelize) => {
const transaction = await queryInterface.sequelize.transaction();
try {
await queryInterface.addColumn(
'users',
'verified',
{ type: Sequelize.BOOLEAN, defaultValue: false },
{ transaction }
);
await queryInterface.sequelize.query(
'UPDATE users SET verified = true WHERE email_verified_at IS NOT NULL',
{ transaction }
);
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
},
down: async (queryInterface) => {
await queryInterface.removeColumn('users', 'verified');
}
};
基于检查点的恢复
请使用数据库引擎经过测试的备份/快照和时间点恢复流程,并在一次性恢复环境中验证。迁移前,请记录恢复点、架构、约束、索引、序列、权限和应用兼容性。CREATE TABLE AS SELECT 复制并非完整备份,绝不能用于自动错误恢复期间删除并重建正在使用的表。此操作会遗漏重要的架构属性,并可能丢失并发写入。部分状态不确定时,请停止操作,并在重试前完成协调。
零停机迁移
蓝绿部署策略
// Phase 1: Make changes backward compatible
module.exports = {
up: async (queryInterface, Sequelize) => {
// Add new column (both old and new code can work)
await queryInterface.addColumn('users', 'email_new', {
type: Sequelize.STRING
});
}
};
// Phase 2: Deploy code that writes to both columns
// Phase 3: Backfill data
module.exports = {
up: async (queryInterface) => {
await queryInterface.sequelize.query(`
UPDATE users
SET email_new = email
WHERE email_new IS NULL
`);
}
};
// Phase 4: Deploy code that reads from new column
// Phase 5: Remove old column
module.exports = {
up: async (queryInterface) => {
await queryInterface.removeColumn('users', 'email');
}
};
跨数据库迁移
从 PostgreSQL 迁移到 MySQL
// Handle differences
module.exports = {
up: async (queryInterface, Sequelize) => {
const dialectName = queryInterface.sequelize.getDialect();
if (dialectName === 'mysql') {
await queryInterface.createTable('users', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
data: {
type: Sequelize.JSON // MySQL JSON type
}
});
} else if (dialectName === 'postgres') {
await queryInterface.createTable('users', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
data: {
type: Sequelize.JSONB // PostgreSQL JSONB type
}
});
}
}
};
资源
- references/orm-switching.md:ORM 迁移指南
- references/schema-migration.md:Schema 转换模式
- references/data-transformation.md:数据迁移脚本
- references/rollback-strategies.md:回滚流程
- assets/schema-migration-template.sql:SQL 迁移模板
- assets/data-migration-script.py:数据迁移实用工具
- scripts/test-migration.sh:迁移测试脚本
最佳实践
- 规划恢复方案:仅在确实可逆时提供经过测试的 down();否则,记录前向修复方案或经过测试的恢复方案,以及数据丢失边界
- 测试迁移:先在预发布环境中测试
- 使用事务:尽可能使用原子迁移
- 先备份:迁移前务必备份
- 小步变更:拆分为小的渐进步骤
- 监控:部署期间留意错误
- 记录文档:说明原因和做法
- 幂等性:迁移应可重复运行
常见问题
- 未测试回滚流程
- 未制定停机策略便进行破坏性变更
- 忘记处理 NULL 值
- 未考虑索引性能
- 忽略外键约束
- 一次迁移过多数据
限制
- 仅当任务明确符合上述范围时,才使用此技能。
- 不要将输出视为环境特定验证、测试或专家审查的替代品。
- 如果缺少必要输入、权限、安全边界或成功标准,请暂停并寻求澄清。