首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Rails Migrations 數據遷移完全指南:創建、回滾、數據變更、模型使用與性能優化

     編輯:Java 相關知識

Migration 本質

Migration 是繼承 ActiveRecord::Migration 的 Ruby 類,用 DSL 描述數據庫結構變更,實現:

  • 版本控制數據庫 Schema。
  • 跨數據庫平台遷移(MySQL/PostgreSQL/SQLite)。
  • 團隊協作同步結構變更。

核心 DSL 方法

方法用途可逆
create_table創建表✅
drop_table刪除表✅ (需指定塊)
add_column添加列✅
remove_column刪除列❌ (需在 down 指定類型)
change_column修改列類型/默認值❌
rename_column重命名列✅
add_index添加索引✅
remove_index刪除索引✅
add_timestamps添加 created_at/updated_at✅
change_table批量修改表✅

兩種編寫風格

1. 舊風格:up / down 成對定義

class AddFlagToProduct < ActiveRecord::Migration
  def up
    add_column :products, :flag, :boolean
    Product.update_all(flag: false)
  end
  
  def down
    remove_column :products, :flag
  end
end

2. 新風格:change 單一方法(Rails 3.1+ 推薦)

class AddFlagToProduct < ActiveRecord::Migration
  def change
    add_column :products, :flag, :boolean, default: false, null: false
    # update_all 不可逆,不能寫在 change 中
  end
end

change 只支持可逆操作(見上表)。數據變更、不可逆 DDL 必須用 up/down。

生成 Migration

# 模型級(自動生成 create_table)
rails g model Product name:string description:text

# 獨立遷移
rails g migration AddPartNumberToProducts part_number:string
rails g migration RemovePartNumberFromProducts part_number:string  # 自動生成 remove_column
rails g migration AddDetailsToProducts part_number:string price:decimal

文件名:YYYYMMDDHHMMSS_migration_name.rb,時間戳保證順序唯一。

運行與回滾

# 運行所有未執行遷移
rake db:migrate

# 指定版本(向上/向下)
rake db:migrate VERSION=20120101000000

# 回滾最後 1 次
rake db:rollback

# 回滾最後 3 次
rake db:rollback STEP=3

# 重做最後 3 次(回滾 + 重跑)
rake db:migrate:redo STEP=3

# 重置數據庫(drop + create + migrate)
rake db:reset

在 Migration 中使用 Model:陷阱與解法

問題場景

# 遷移 1
class AddFlagToProduct < ActiveRecord::Migration
  def change
    add_column :products, :flag, :boolean
    Product.update_all(flag: false)  # 使用當前代碼的 Product 模型
  end
end

# 遷移 2(同事稍後添加)
class AddFuzzToProduct < ActiveRecord::Migration
  def change
    add_column :products, :fuzz, :string
    Product.update_all(fuzz: 'fuzzy')
  end
end

# Model 當前狀態
class Product < ActiveRecord::Base
  validates :flag, :fuzz, presence: true
end

新同事拉取代碼執行 rake db:migrate:

  1. 運行遷移 1 → Product 模型驗證要求 fuzz 存在 → 報錯 undefined method 'fuzz'。
  2. 遷移 2 尚未運行,數據庫無 fuzz 列,模型緩存的列信息也不含 fuzz。

解法:定義局部空模型 + 重置列緩存

class AddFlagToProduct < ActiveRecord::Migration
  # 定義遷移專用模型,隔離驗證
  class Product < ActiveRecord::Base; end
  
  def change
    add_column :products, :flag, :boolean
    Product.reset_column_information  # 刷新緩存,識別新列
    Product.update_all(flag: false)
  end
end

class AddFuzzToProduct < ActiveRecord::Migration
  class Product < ActiveRecord::Base; end
  
  def change
    add_column :products, :fuzz, :string
    Product.reset_column_information
    Product.update_all(fuzz: 'fuzzy')
  end
end

原則:遷移中操作數據時,始終定義局部模型類,避免受應用代碼驗證/回調影響。

輔助工具

t.timestamps

create_table :products do |t|
  t.timestamps  # 自動添加 created_at, updated_at (datetime, null: false)
end

change_table :products do |t|
  t.timestamps
end

t.references(外鍵快捷方式)

create_table :products do |t|
  t.references :category  # 生成 category_id (bigint), 添加索引
end

# 多態引用
create_table :pictures do |t|
  t.references :imageable, polymorphic: { default: 'Photo' }
  # 生成 imageable_id, imageable_type (string, default: 'Photo')
end

suppress_messages / say / say_with_time 控制輸出

class CreateProducts < ActiveRecord::Migration
  def change
    suppress_messages do
      create_table :products do |t|
        t.string :name
        t.text :description
        t.timestamps
      end
    end
    say "Created products table"
    suppress_messages { add_index :products, :name }
    say "Added index!", true
    say_with_time 'Waiting...' do
      sleep 10
      250
    end
  end
end

Schema.rb 與 SQL 結構文件

  • db/schema.rb:Ruby DSL 格式,默認輸出,跨數據庫可移植。
  • db/structure.sql:數據庫原生 SQL(pg_dump/mysqldump),保留視圖、存儲過程、觸發器等高級對象。

配置:config.active_record.schema_format = :sql 或 :ruby(默認)。

不要手動編輯 schema.rb,它是 rake db:migrate 自動生成的單一真實源。

大表遷移性能優化

  1. 避免長事務:大表 add_column 默認值會鎖表重寫全表。
    → PostgreSQL 11+ / MySQL 8.0+ 支持瞬時添加帶默認值的列。
    → 舊版本:分步驟(加列無默認值 → 分批次更新 → 加默認值/非空約束)。
  2. 索引創建併發:add_index :table, :col, algorithm: :concurrently (PG) / LOCK=NONE (MySQL 8.0+)。
  3. 數據回填分批:Product.where(flag: nil).find_in_batches { |batch| batch.update_all(flag: false) }。
  4. 零停機部署:遷移與代碼發布解耦,先部署兼容新舊 Schema 的代碼,再跑遷移,最後部署移除舊代碼邏輯。

總結

Migration 是 Rails 數據庫演進的基石。關鍵實踐:

  1. 優先用 change,不可逆操作用 up/down。
  2. 數據變更必須定義局部空模型 + reset_column_information。
  3. 善用 references、timestamps 等輔助方法減少樣板代碼。
  4. 大表變更採用分步驟、併發索引、分批回填,配合零停機發布流程。
  5. schema.rb 為唯一真實源,嚴禁手改。

「數據庫遷移如同飛機維修:必須在飛行中更換引擎,容不得半點差錯。」

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved