关于 rails-migrations
rails-migrations 是用于规划和审查 Rails 安全架构变更与数据迁移的技能,涵盖可逆变更、分阶段发布,以及将高风险回填移出部署迁移窗口。
主要功能
- 01可逆迁移指引
- 02安全的架构与索引变更
- 03脚本批量回填
- 04多租户索引策略
- 05分阶段发布方案
适用场景
- 01审查迁移的锁表风险
- 02规划大型数据回填
- 03分多个部署替换列
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ marckohlbrugge/ 37signals-skills --skill rails-migrations安装后在 Claude Code 中输入/rails-migrations确认已加载。
详细介绍
Rails 迁移
用于架构变更和迁移安全。
规则
- 尽可能确保迁移可逆(数据迁移使用带有显式 up/down SQL 的
reversible块)。 - 在迁移中使用原始 SQL 操作数据;避免引用可能随时间发生变化的应用模型。
- 避免在大表上长时间持有表锁;将有风险的操作拆分为多个可安全部署的步骤。
- 将架构变更与大量数据回填分开。
- 优先使用重复运行仍然安全的数据库操作(在迁移可能面对不同状态运行时,使用
table_exists?/column_exists?守卫)。 - 小规模的内联回填可以放在同一个迁移中(添加列 →
execute "UPDATE ..."→ 收紧约束);任何耗时较长的操作都应完全移出迁移流程。
脚本回填(并非所有操作都通过 db:migrate 执行)
耗时较长或有风险的数据回填放在 script/migrations/*.rb 中,手动运行(例如通过 Kamal),而不是在部署迁移窗口内运行:
- 在文件头注释中说明前置条件和运行说明。
- 变更数据前先运行预检查询,打印操作范围。
- 保持幂等:跳过已处理的行(
next if Entry.exists?(...)),确保重新运行安全。 - 分批处理(
find_each/in_batches),绝不一次性写入全部数据。
安全模式
- 添加可空列 → 回填 → 强制
NOT NULL。 - 在支持或需要时并发添加索引。
- 在添加唯一索引之前,立即在同一个迁移中对数据去重(使用原始 SQL 删除较旧的重复记录)。
- 仅变更数据的迁移也是合理的:向上迁移使用
find_each+update!,向下迁移使用update_all。 - 使用分批回填,而不是一次性写入全部数据。
约束:应审慎选择,而不是默认启用
- 优先使用数据库级唯一索引,而不是 AR
validates uniqueness(验证存在竞态条件;索引不存在此问题)。 - 外键需要权衡:Fizzy 有意移除了所有外键(在引用上使用
foreign_key: false),以加快 DDL 并提高分片友好性,同时由 Rails 保证完整性。两种做法都可以,但应保持一致并记录说明,而不是无意为之。 - 对于不便使用 CHECK 约束的特殊不变量,可通过成本低的架构技巧来强制执行:例如,在单例表中使用默认值为 0 的
singleton_guard列,并为其创建唯一索引。 - 原子化的租户级计数器:使用
account.increment!(:cards_count)生成序列号,并创建唯一[account_id, number]索引;全局 ID 序列则使用加锁的序列行(在锁内执行first_or_create!、increment!)。
多租户索引策略
- 为应用引入租户隔离时,在专门的迁移阶段将全局索引替换为
[account_id, ...]复合索引;删除现已多余的单列索引,并说明原因。 - 范围唯一性应在数据库级别实现:
add_index :tags, [:account_id, :title], unique: true。
多数据库 / 多适配器应用
- Solid Queue/Cache/Cable 各自使用独立数据库,并配置单独的
migrations_paths和架构文件。 - 在同一代码库中支持 SQLite + MySQL:在迁移中使用提前返回的适配器守卫(
return if connection.adapter_name == "SQLite")、适配器专用 DDL(FTS5 与分片全文搜索),并通过各配置的schema_dump同时导出架构(schema.rb/schema_sqlite.rb)。 - 在生产环境使用 SQLite 时,设置
default_transaction_mode: immediate,以减少并发写入时出现的SQLITE_BUSY。 - 在生产环境中禁用
dump_schema_after_migration。
分阶段发布操作指南
-
替换列
- 部署 1:添加新的可空列。
- 部署 2:双写 / 回填。
- 部署 3:从新列读取。
- 部署 4:强制执行约束,之后再删除旧列。
-
强化约束
- 先清理数据 / 回填。
- 数据符合要求后,再添加索引 / 约束。
- 约束生效后,再调整应用行为以依赖该约束。
-
破坏性变更
- 首先在应用代码中弃用相关读写操作。
- 在单独的一次部署中移除相关用法。
- 确认观察期结束后,再删除列 / 表。
危险信号
- 没有明确理由就执行不可逆迁移。
- 将模式重写和大量数据迁移合并到同一步。
- 在事务密集型默认迁移中执行大量回填(移至
script/migrations/)。 - 未分阶段弃用就删除列或表。
- 在迁移中引用应用模型(模型行为会发生变化;请使用 SQL 或内联最精简的 AR 类)。
- 未先清理重复数据就添加唯一索引。
- 使用
validates uniqueness,但没有对应的唯一索引。
评论
支持 Markdown