0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Active Record】データ処理のアンチパターン①

Last updated at Posted at 2023-09-04

Active Recordデータ処理のアンチパターンについて整理されたスライドを見つけたので、学習のためにまとめました。

今回読んだスライドがこちらです。
3つの事例と6つのアンチパターンを紹介していただいているので、1つずつ見て整理して行きます。


前提事項
・User has many Postsな関係のUserモデル&Postモデル
・User10万件、Post50万件のデータが登録されている
・データベースの最適化は考慮しない

スキーマ定義

create_table "posts", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
    t.bigint "user_id, null: false
    t.string "title"
    t.text "content"
    t.integer "like_count", default: 0, null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["user_id"], name: "index_posts_on_user_id"
end

create_table "users", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
    t.string "email", null: false
    t.string "name", null: false
    t.integer "point", default: 0, null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["email"], name: "index_users_on_email", unique: true
end

add_foreign_key "posts", "users"

事例1 全ユーザの中から、2017以降の登録ユーザへ100ポイント付与する

アンチパターン

User.all.each do |user|
  if user.created_at >= Date.new(2017)
    user.point += 100
    user.save
  end
end

All Each Pattern
Model.allで、テーブルの全レコードを取得した後に、その結果をeachでループさせること。

問題点 解決策
全件取得でメモリが逼迫。そもそも全件取得する必要あるのか? 取得件数をフィルター(all→where)
ループ回数が増えるのでCPUリソースも消費 少しずつUserを取得してメモリフレンドリーな処理に (each→fnd_each)

改善後ソースコード

User.where("created_at>=?", Date.new(2017)) # 取得件数のフィルタリング
    .find_each do |user|                    # 少しずつレコードを取得する
        user.point += 100
        user.save
end

N+1 Update Queries Pattern

問題点 解決策
1回Select+N回Updateのクエリが走る 複数レコードを一括で更新する(update→update_all)
Nの数が多くなるほどパフォーマンスが悪化する update_allはバリデーションやコールバックが実行しないので、適切な処理を加えた上で使う必要がある。

改善後ソースコード

User.where("created_at>=?", Date.new(2017))
    .update_all("point = point + 100")

まとめ
・全件取得はなるべく避け、事前にフィルタリングできないか検討する
・ループ処理はCPUリソースを消費するので、find_eachメソッドなどで少しずつ処理する
・update_allを使用することで、クエリの発行回数を削減する

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?