手动修改
错误:直接修改生产数据库 正确:使用迁移脚本
来源: everything-claude-code 技能库
database-migrations 涵盖数据库迁移的最佳实践,包括 Schema 变更、数据迁移、回滚和零停机部署。
在应用任何迁移之前:
-- 好:可空列,无锁ALTER TABLE users ADD COLUMN avatar_url TEXT;
-- 好:带默认值的列(Postgres 11+ 即时完成,无需重写)ALTER TABLE users ADD COLUMN is_active BOOLEAN NOT NULL DEFAULT true;
-- 不好:无默认值的 NOT NULL(需要全表重写)ALTER TABLE users ADD COLUMN email VARCHAR(255) NOT NULL;-- 好:先标记废弃,再删除-- 步骤 1: 添加新列,迁移数据ALTER TABLE users ADD COLUMN new_email VARCHAR(255);UPDATE users SET new_email = email WHERE email IS NOT NULL;
-- 步骤 2: 切换引用-- (修改代码使用新列)
-- 步骤 3: 删除旧列ALTER TABLE users DROP COLUMN email;-- 好:并发创建索引(不锁表)CREATE INDEX CONCURRENTLY idx_users_email ON users(email);
-- 不好:普通创建索引(锁表)CREATE INDEX idx_users_email ON users(email);-- 好:使用锁超时避免长时间锁SET lock_timeout = '1s';ALTER TABLE orders ADD COLUMN status VARCHAR(50);
-- 好:分批更新大数据UPDATE orders SET status = 'pending'WHERE id BETWEEN 1 AND 100000;-- 重复直到完成-- 检查表大小SELECT schemaname, relname, n_live_tup as row_count, pg_size_pretty(pg_total_relation_size(relid)) as sizeFROM pg_stat_user_tablesORDER BY n_live_tup DESCLIMIT 10;-- 策略 1:分批处理DO $$DECLARE batch_size INTEGER := 10000; total_updated INTEGER := 0;BEGIN LOOP UPDATE users SET processed = true WHERE id IN ( SELECT id FROM users WHERE processed = false ORDER BY id LIMIT batch_size );
GET DIAGNOSTICS total_updated = ROW_COUNT;
IF total_updated < batch_size THEN EXIT; END IF;
PERFORM pg_sleep(0.1); -- 让其他操作有机会 END LOOP;END $$;-- 迁移后验证-- 1. 检查空值SELECT COUNT(*) FROM users WHERE email IS NULL AND email_new IS NOT NULL;
-- 2. 检查一致性SELECT COUNT(*) FROM orders oLEFT JOIN users u ON o.user_id = u.idWHERE u.id IS NULL;
-- 3. 检查约束ALTER TABLE users ADD CONSTRAINT email_unique UNIQUE (email_new);# 永远不要编辑已存在的迁移# 正确:创建新的迁移来撤销
# 2024_01_15_001_add_users_table.py (原迁移)def upgrade(): op.create_table('users', op.Column('id', sa.Integer()), op.Column('email', sa.String(255)), )
# 2024_01_20_001_rollback_users_table.py (新迁移撤销)def upgrade(): op.drop_table('users')-- 1. 在新表上工作CREATE TABLE users_new ( ... );
-- 2. 同步数据INSERT INTO users_new SELECT * FROM users;
-- 3. 切换ALTER TABLE users RENAME TO users_old;ALTER TABLE users_new RENAME TO users;
-- 4. 验证-- 5. 删除旧表DROP TABLE users_old;✅ 正确做法:1. 先添加可空列2. 迁移数据3. 修改代码使用新列4. 添加 NOT NULL 约束5. 删除旧列
❌ 错误做法:1. 直接添加 NOT NULL 列2. 在大表上操作3. 不测试✅ 正确做法:1. 使用 CONCURRENTLY 关键字2. 设置锁超时3. 在低峰期执行4. 验证索引使用
❌ 错误做法:1. 普通 CREATE INDEX2. 在高峰期间操作3. 不检查锁等待✅ 正确做法:1. 预估数据量2. 分批处理3. 验证数据完整性4. 准备回滚
❌ 错误做法:1. 一次性更新全表2. 不检查约束3. 忽略外键关系# Alembic (Python)alembic revision --autogenerate -m "add column"alembic upgrade headalembic downgrade -1
# Prismaprisma migrate devprisma migrate deployprisma migrate reset
# Djangopython manage.py makemigrationspython manage.py migrate
# golang-migratemigrate -path=migrations -database postgres://localhost/db up
# SQLpsql -d dbname -f migration.sql手动修改
错误:直接修改生产数据库 正确:使用迁移脚本
大表锁表
错误:普通 ALTER 操作 正确:使用 CONCURRENTLY
混合迁移
错误:DDL 和 DML 在一起 正确:分开执行
不测试
错误:在生产直接运行 正确:在备份数据上测试
何时使用
何时不用
关键要点
常见错误
| ORM | 命令 |
|---|---|
| Prisma | prisma migrate dev |
| Django | python manage.py makemigrations |
| Alembic | alembic revision --autogenerate |
| Kysely | kysely-codegen |
| TypeORM | typeorm migration:generate |
| 技能 | 关系 |
|---|---|
| postgres-patterns | PostgreSQL 模式 |
| python-testing | 测试驱动开发 |
| code-review | 代码审查 |
makemigrations官方原文: database-migrations SKILL.md
💡 提示:数据库迁移就像做手术,务必做好准备再动手。