最初に最も明白なアプローチから始めましょう:
type_a_task_ids = [1,2,3,1,2,3]
type_b_task_ids = [1,2,2,3,3]
type_a_tasks = type_a_task_ids.map { |task_id| Task.includes(:project).find(task_id) }
type_b_tasks = type_b_task_ids.map { |task_id| Task.includes(:project).find(task_id) }
上記は単純で読みやすいですが、潜在的に低速です。特定のタスクで、個別ごとに1つのデータベースラウンドトリップと、個別ごとに1つのデータベースラウンドトリップを実行task_id します。project_idすべてのレイテンシーが合計されるため、タスク(および対応するプロジェクト)をまとめてロードする必要があります。
Railsをバルクロード(プリフェッチ)して、同じレコードを2回のラウンドトリップ(1つはすべての個別のタスク用、もう1つはすべての個別の関連プロジェクト用)で事前にキャッシュしてから、正確なものを取得できれば素晴らしいと思います。上記と同じコード-findデータベースではなく常にキャッシュにヒットすることを除いて。
残念ながら、RailsではクエリキャッシュActiveRecord を使用するため、(デフォルトでは)そのようには機能しません。最初のクエリは2番目のクエリとは異なるため、()の後に()を実行しても、クエリキャッシュは活用さTask.find(1)れSELECT * FROM tasks WHERE id=1ませTask.find([1,2,3])ん。SELECT * FROM tasks WHERE id IN (1,2,3)(ただし、Railsはまったく同じクエリが複数回実行され、キャッシュされた結果セットを返すためTask.find(1)、2回目、3回目などの時間を実行すると、クエリキャッシュが活用されます。)SELECT
IdentityMapキャッシュを入力します。Identity Map Cachingは、テーブルごとおよび主キーごとに、クエリではなくレコードをキャッシュするという意味で異なります。したがって、実行Task.find([1,2,3])すると、テーブルのIDマップキャッシュに3つのレコード(それぞれIDをtasks持つエントリ)が入力され、その後、実行すると、テーブルとIDのキャッシュされたレコードがすぐに返されます。123Task.find(1)tasks1
# with IdentityMap turned on (see IdentityMap documentation)
# prefetch all distinct tasks and their associated projects
# throw away the result, we only want to prep the cache
Task.includes(:project).find(type_a_task_ids & type_b_task_ids)
# proceed with regular logic
type_a_task_ids = [1,2,3,1,2,3]
type_b_task_ids = [1,2,2,3,3]
type_a_tasks = type_a_task_ids.map { |task_id| Task.includes(:project).find(task_id) }
type_b_tasks = type_b_task_ids.map { |task_id| Task.includes(:project).find(task_id) }
ただし、IdentityMap (正当な理由で)デフォルトでアクティブになったことはなく、最終的にRailsから削除されました。
なしで同じ結果をどのように達成しますIdentityMapか?単純:
# prefetch all distinct tasks and their associated projects
# store the result in our own identity cache
my_tasks_identity_map = \
Hash[Task.includes(:project).find(type_a_task_ids & type_b_task_ids).map { |task|
[ task.id, task ]
}]
# proceed with cache-centric logic
type_a_task_ids = [1,2,3,1,2,3]
type_b_task_ids = [1,2,2,3,3]
type_a_tasks = type_a_task_ids.map { |task_id| my_tasks_identity_map[task_id] }
type_b_tasks = type_b_task_ids.map { |task_id| my_tasks_identity_map[task_id] }