将更新频率较低的列分离到另一个表中进行重构Rails

在什么时候有效?

    • 1つのテーブルの中に更新/参照頻度が高いカラムと低いカラムが混在している

 

    1つのmodelに大量のビジネスロジックがあって可読性が低い

提取列的好处

    • memcache などキャッシュを使っている場合に無駄が減る

参照頻度の高いデータだけキャッシュされるべき

更新頻度の多い情報のサイズは小さいほうが良い

DBのメモリになるべく乗るように

以下是一个例子。

在用户模型中,包含着更新/参考频率较低的关于个人资料的信息。

    • zipcode

 

    • prefecture_code

 

    • city_code

 

    address

用户模型

$ rails g scaffold user name:string mail:string zipcode:string prefecture_code:integer city_code:integer address:string
  create_table "users", force: :cascade do |t|
    t.string   "name"
    t.string   "mail"
    t.string   "zipcode"
    t.integer  "prefecture_code"
    t.integer  "city_code"
    t.string   "address"
    t.datetime "created_at",      null: false
    t.datetime "updated_at",      null: false
  end

将桌子分成几部分。

创建个人资料模型

$ rails g model profile zipcode:string prefecture_code:integer city_code:integer address:string user_id:integer

2. 将Profile模型和User模型进行关联

class Profile < ActiveRecord::Base
  belongs_to :user
end

通过委托 Profile model,无需修改现有代码。

class User < ActiveRecord::Base
  # delegateすることで、Profile モデルの中身を User から取得できる 
  delegate :zipcode, :prefecture_code, :city_code, :address, to: :profile
  has_one :profile
end

3. 创造迁移

    1. 创建Profile表

 

    1. 将User模型中的信息转移到Profile表中

 

    从User表中删除移动到Profile表的列
class CreateProfiles < ActiveRecord::Migration
  def up
    # Profileテーブルを作る
    create_table :profiles do |t|
      t.string :zipcode
      t.integer :prefecture_code
      t.integer :city_code
      t.string :address
      t.integer :user_id

      t.timestamps null: false
    end

    # Profileテーブルに移行する情報をUserモデルからコピー
    User.all.each do |user|
      Profile.create(
        user:            user,
        zipcode:         user.zipcode,
        prefecture_code: user.prefecture_code,
        city_code:       user.city_code,
        address:         user.address
      )
    end

    # Userテーブルから、Profileテーブルに移ったカラムを削除
    remove_column :users, :zipcode
    remove_column :users, :prefecture_code
    remove_column :users, :city_code
    remove_column :users, :address
  end

  def down
    add_column :users, :zipcode
    add_column :users, :prefecture_code
    add_column :users, :city_code
    add_column :users, :address

    # Profileモデルの情報をUserモデルに移行
    Profile.each do |profile|
      profile.user.update(
        zipcode:         profile.zipcode,
        prefecture_code: profile.prefecture_code,
        city_code:       profile.city_code,
        address:         profile.address
      )
    end

    drop_table :profiles
  end
end

只有在需要Profile信息时,才会查阅Profile表。

irb(main):010:0> User.all.first.zipcode
  User Load (0.3ms)  SELECT  "users".* FROM "users"  ORDER BY "users"."id" ASC LIMIT 1
  Profile Load (0.1ms)  SELECT  "profiles".* FROM "profiles" WHERE "profiles"."user_id" = ? LIMIT 1  [["user_id", 1]]
=> "419-4284"

irb(main):011:0> User.all.first.name
  User Load (0.2ms)  SELECT  "users".* FROM "users"  ORDER BY "users"."id" ASC LIMIT 1
=> "木村 優衣"

接下来应该做的事情是什么?

    必然性があまりないのに、Profileの情報を参照している部分を修正する

代码 (daima)

    https://github.com/kasei-san/refactor_model

请参考

プライマリキーを使った1:1関連でカラム数の多いテーブルを分割する – Hidden in Plain Sight

必ず1対1であれば、わざわざ関連付けなくても、同じ値のプライマリキーを使うという方法

ソーシャルゲームのためのMySQL入門その2 – Technology of DeNA

广告
将在 10 秒后关闭
bannerAds