将更新频率较低的列分离到另一个表中进行重构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. 创造迁移
-
- 创建Profile表
-
- 将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