数据库迁移:安全管理 Dev 与生产环境

发布: (2025年12月8日 GMT+8 12:37)
7 min read
原文: Dev.to

Source: Dev.to

这是 2025 年独立构建 SaaS – 设计、实现与运营倒计时日历 第 8 天。

文章中描述的方法是我通过反复试验得出的。如果你知道更好的办法,请在评论中告诉我。

常见的迁移管理方式

管理数据库迁移有多种方法。

使用 ORM 的迁移功能

像 Drizzle ORM、Prisma 和 TypeORM 这样的 ORM 都内置了迁移功能。

# For Drizzle ORM
npx drizzle-kit generate  # Generate migrations from schema
npx drizzle-kit migrate   # Apply migrations

当你在 TypeScript 中编写表定义时,ORM 会检测变化并自动生成诸如 ALTER TABLE 的 SQL。

好处

  • 自动根据代码变更生成迁移 SQL
  • 在数据库表中管理应用历史
  • 可通过单条命令执行

挑战

  • 难以处理复杂的数据迁移(例如转换已有数据)
  • 有时难以了解将要执行的内容

管理多个环境时的挑战

ORM 的迁移功能在单一环境下使用非常方便,但在开发环境和生产环境分离的情况下会出现挑战:

  • 开发与生产的模式可能不同步
  • 难以追踪 “已在开发环境执行但尚未在生产环境执行” 的状态
  • 难以知道何时执行了哪些迁移

我在开发 Memoreru(我的独立项目)时也遇到了这些问题,经过反复试验形成了目前的运营规则。

独立项目的迁移管理

顺序文件管理

迁移文件由 Claude Code 生成,并使用顺序编号进行管理。

database/migrations/
├── sql/
│   ├── 001_create_users_table.sql
│   ├── 002_create_posts_table.sql
│   ├── 003_add_user_profile.sql
│   ├── 004_add_status_column.sql
│   └── …
├── scripts/
│   └── migrate.sh
├── status.json
└── README.md

顺序管理的好处

  • 一眼即可看出执行顺序
  • 文件名显示模式对应的时间点
  • 易于辨别生产与开发之间的差异

为什么直接管理 SQL 文件

我不使用 drizzle-kit generate,而是直接创建 SQL 文件。模式定义仍然交由 Drizzle ORM 管理,以保持类型安全。

直接管理 SQL 文件的原因

  • 更容易处理复杂变更(涉及数据迁移的情况)
  • 完全了解将要执行的内容
  • 故障排查更简洁

共享的开发/生产迁移脚本

为什么使用共享脚本

# Development environment
./database/migrations/scripts/migrate.sh dev 004_add_status_column.sql

# Production environment
./database/migrations/scripts/migrate.sh pro 004_add_status_column.sql

共享脚本的好处

  • 演练效果 – 在开发环境运行相同流程,可在生产前发现问题
  • 统一流程 – 避免因工作流不同导致的意外
  • 集中日志 – 两个环境的执行日志采用相同格式

环境特有的差异

项目开发环境生产环境
连接信息自动从 .env.local 加载每次手动输入
备份建议显示警告;需要手动备份

每次输入生产环境的连接字符串虽然繁琐,但它充当了一道 安全措施,防止误操作到错误的数据库。我也避免把生产库凭证交给 Claude Code,从而消除 AI 误操作的风险。迁移前,我会在 pgAdmin 中手动备份两种环境的数据库。

安全机制

脚本包含:

  • 确认流程 – 在执行迁移前提示确认
  • 连接测试 – 先验证数据库连通性
  • 自动日志保存 – 将日志写入 logs/migrations/ 以供后续审查

具体实现由 Claude Code 生成;你只需描述需求,让它产出合适的脚本即可。

使用 status.json 的集中状态管理

{
  "lastUpdated": "2025-12-04",
  "environments": {
    "dev": {
      "name": "Development",
      "lastApplied": "004_add_status_column",
      "appliedAt": "2025-12-04"
    },
    "pro": {
      "name": "Production",
      "lastApplied": "003_add_user_profile",
      "appliedAt": "2025-11-30"
    }
  },
  "pending": {
    "pro": ["004_add_status_column"]
  }
}

检查生产环境的待执行迁移

# Display pending list
jq '.pending.pro' database/migrations/status.json
# => ["004_add_status_column"]

一眼即可看出哪些迁移已在开发环境执行但尚未在生产环境执行。

自动更新

迁移执行完毕后,脚本会自动更新 status.json,避免手动编辑导致的遗漏。

实用技巧

技巧 1:逐步进行破坏性变更

-- Step 1: Add new column
ALTER TABLE contents ADD COLUMN new_name TEXT;

-- Step 2: Migrate data
UPDATE contents SET new_name = old_name;

-- Step 3: Drop old column (in a separate migration)
ALTER TABLE contents DROP COLUMN old_name;

在第 2 步和第 3 步之间检查应用行为,可在出现问题时将影响降到最低。

技巧 2:准备回滚 SQL

-- Migration
ALTER TABLE contents ADD COLUMN status TEXT DEFAULT 'draft';

-- Rollback (execute only when needed)
-- ALTER TABLE contents DROP COLUMN status;

将回滚语句保留为注释,便于快速查阅。

技巧 3:与 Claude Code 的协作规则

CLAUDE.md 中记录迁移操作规则:

## Migration Operations

- Don't execute SQL directly with `psql`  
- Always apply via `migrate.sh` script  
- Rehearse in dev environment before production  
- Commit `status.json` after applying

这些规则可防止 AI 代理误执行原始 SQL。

总结

运行良好的方面

  • 使用顺序文件管理实现时间线追踪
  • 通过共享的 dev/production 脚本进行演练
  • 通过 status.json 实现集中状态管理
  • 确认流程防止误操作

需要注意的地方

  • 随着变更量增大,手动管理 SQL 可能变得繁琐
  • 先在具有代表性的测试数据上验证复杂数据迁移
  • 提前规划回滚方案

即使是独立开发,早期就建立清晰的迁移规则也能帮助后期避免许多问题。

Back to Blog

相关文章

阅读更多 »