1

В настоящее время мой код выглядит как это:Ради эффективности и оптимизации, следует ли использовать eager_load или включать?

current_user.association.includes(a: [:b, {c: :d}, {e: :f}]).to_a

При выполнении вызова, кажется, каждый включает в себя называются через свой собственный SELECT призыв к БД.

Однако, когда я делаю current_user.association.eager_load(a: [:b, {c: :d}, {e: :f}]).to_a Я вижу один огромный SELECT звонок.

Прошу, потому что я не видел этого раньше. Я бы предположил, что eager_load более эффективен из-за меньших вызовов БД.

+0

Я хотел бы предложить либо спрашивать это в чате. Эффективность _working code_ - небольшая тема для переполнения стека. – onebree

ответ

2

Поскольку я не могу вывести запрос из вашего описания (a: [:b, {c: :d}, {e: :f}]), мне нужно поговорить о includes для немного.

includes - метод запроса, который размещается в разных ситуациях.

Вот несколько примеров кода:

# model and reference 
class Blog < ActiveRecord::Base 
    has_many :posts 

    # t.string "name" 
    # t.string "author" 
end 

class Post < ActiveRecord::Base 
    belongs_to :blog 

    # t.string "title" 
end 

# seed 
(1..3).each do |b_id| 
    blog = Blog.create(name: "Blog #{b_id}", author: 'someone') 
    (1..5).each { |p_id| blog.posts.create(title: "Post #{b_id}-#{p_id}") } 
end 

В одном случае он стреляет два отдельных запросов, так же, как preload.

> Blog.includes(:posts) 
    Blog Load (2.8ms) SELECT "blogs".* FROM "blogs" 
    Post Load (0.7ms) SELECT "posts".* FROM "posts" WHERE "posts"."blog_id" IN (1, 2, 3) 

В другом случае, когда запрос на указанной таблице, он срабатывает только один LEFT OUTER JOIN запрос, так же, как eager_load.

> Blog.includes(:posts).where(posts: {title: 'Post 1-1'}) 
    SQL (0.3ms) SELECT "blogs"."id" AS t0_r0, "blogs"."name" AS t0_r1, "blogs"."author" AS t0_r2, "blogs"."created_at" AS t0_r3, "blogs"."updated_at" AS t0_r4, "posts"."id" AS t1_r0, "posts"."title" AS t1_r1, "posts"."created_at" AS t1_r2, "posts"."updated_at" AS t1_r3, "posts"."blog_id" AS t1_r4 FROM "blogs" LEFT OUTER JOIN "posts" ON "posts"."blog_id" = "blogs"."id" WHERE "posts"."title" = ? [["title", "Post 1-1"]] 

Так что, я думаю, вы можете просить в другой части includes и eager_load, что

Должны ли мы использовать два отдельных запросов или один LEFT OUTER JOIN запрос для эффективности и оптимизации ?

Это также меня смущает. После некоторого рытья я нашел это article Фабио Акита убедил меня. Вот некоторые ссылки и пример:

В некоторых ситуациях внешнее соединение монстра становится медленнее, чем многие более мелкие запросы. Суть в том, что, как правило, лучше разделить монстра на более мелкие. Это позволяет избежать проблем с перегрузкой продукта.

Чем дольше и сложнее результирующий набор, тем больше это имеет значение, потому что больше объектов придется решать с Rails. Выделение и освобождение нескольких сотен или тысяч маленьких дублированных объектов никогда не бывает хорошим делом.

Пример запроса данных из Rails

> Blog.eager_load(:posts).map(&:name).count 
    SQL (0.9ms) SELECT "blogs"."id" AS t0_r0, "blogs"."name" AS t0_r1, "blogs"."author" AS t0_r2, "blogs"."created_at" AS t0_r3, "blogs"."updated_at" AS t0_r4, "posts"."id" AS t1_r0, "posts"."title" AS t1_r1, "posts"."created_at" AS t1_r2, "posts"."updated_at" AS t1_r3, "posts"."blog_id" AS t1_r4 FROM "blogs" LEFT OUTER JOIN "posts" ON "posts"."blog_id" = "blogs"."id" 
=> 3 

Пример для данных SQL вернулся из LEFT OUTER JOIN запроса

sqlite> SELECT "blogs"."id" AS t0_r0, "blogs"."name" AS t0_r1, "blogs"."author" AS t0_r2, "blogs"."created_at" AS t0_r3, "blogs"."updated_at" AS t0_r4, "posts"."id" AS t1_r0, "posts"."title" AS t1_r1, "posts"."created_at" AS t1_r2, "posts"."updated_at" AS t1_r3, "posts"."blog_id" AS t1_r4 FROM "blogs" LEFT OUTER JOIN "posts" ON "posts"."blog_id" = "blogs"."id"; 
1|Blog 1|someone|2015-11-11 15:22:35.015095|2015-11-11 15:22:35.015095|1|Post 1-1|2015-11-11 15:22:35.053689|2015-11-11 15:22:35.053689|1 
1|Blog 1|someone|2015-11-11 15:22:35.015095|2015-11-11 15:22:35.015095|2|Post 1-2|2015-11-11 15:22:35.058113|2015-11-11 15:22:35.058113|1 
1|Blog 1|someone|2015-11-11 15:22:35.015095|2015-11-11 15:22:35.015095|3|Post 1-3|2015-11-11 15:22:35.062776|2015-11-11 15:22:35.062776|1 
1|Blog 1|someone|2015-11-11 15:22:35.015095|2015-11-11 15:22:35.015095|4|Post 1-4|2015-11-11 15:22:35.065994|2015-11-11 15:22:35.065994|1 
1|Blog 1|someone|2015-11-11 15:22:35.015095|2015-11-11 15:22:35.015095|5|Post 1-5|2015-11-11 15:22:35.069632|2015-11-11 15:22:35.069632|1 
2|Blog 2|someone|2015-11-11 15:22:35.072871|2015-11-11 15:22:35.072871|6|Post 2-1|2015-11-11 15:22:35.078644|2015-11-11 15:22:35.078644|2 
2|Blog 2|someone|2015-11-11 15:22:35.072871|2015-11-11 15:22:35.072871|7|Post 2-2|2015-11-11 15:22:35.081845|2015-11-11 15:22:35.081845|2 
2|Blog 2|someone|2015-11-11 15:22:35.072871|2015-11-11 15:22:35.072871|8|Post 2-3|2015-11-11 15:22:35.084888|2015-11-11 15:22:35.084888|2 
2|Blog 2|someone|2015-11-11 15:22:35.072871|2015-11-11 15:22:35.072871|9|Post 2-4|2015-11-11 15:22:35.087778|2015-11-11 15:22:35.087778|2 
2|Blog 2|someone|2015-11-11 15:22:35.072871|2015-11-11 15:22:35.072871|10|Post 2-5|2015-11-11 15:22:35.090781|2015-11-11 15:22:35.090781|2 
3|Blog 3|someone|2015-11-11 15:22:35.093902|2015-11-11 15:22:35.093902|11|Post 3-1|2015-11-11 15:22:35.097479|2015-11-11 15:22:35.097479|3 
3|Blog 3|someone|2015-11-11 15:22:35.093902|2015-11-11 15:22:35.093902|12|Post 3-2|2015-11-11 15:22:35.103512|2015-11-11 15:22:35.103512|3 
3|Blog 3|someone|2015-11-11 15:22:35.093902|2015-11-11 15:22:35.093902|13|Post 3-3|2015-11-11 15:22:35.108775|2015-11-11 15:22:35.108775|3 
3|Blog 3|someone|2015-11-11 15:22:35.093902|2015-11-11 15:22:35.093902|14|Post 3-4|2015-11-11 15:22:35.112654|2015-11-11 15:22:35.112654|3 
3|Blog 3|someone|2015-11-11 15:22:35.093902|2015-11-11 15:22:35.093902|15|Post 3-5|2015-11-11 15:22:35.117601|2015-11-11 15:22:35.117601|3 

Мы получили ожидаемый результат от Rails, но больший результат от SQL. И это потеря эффективности для LEFT OUTER JOIN.

Итак, мой заключение есть, предпочтител includes более eager_load.


Я заключил блоге о Preload, Eager_load, Includes, References, and Joins in Rails во время исследования. Надеюсь, это поможет.

Ссылка

+0

Да, я тоже пришел к выводу, что это определенно предпочтительнее. eager_load следует использовать только в определенных случаях, и я бы рекомендовал тестирование, чтобы убедиться, что вы должны использовать eager_load, где, по вашему мнению, вам это нужно. Я выполнил свои собственные тесты по этому вопросу, и eager_load занимает гораздо больше времени, чем во многих случаях. Как показано в другом ответе, для 4000 записей сложный запрос может занимать в 36 раз больше времени с помощью eager_load и include. – David

0

Итак, как оказалось, в какой-то момент ActiveRecord фактически попытался получить все в один запрос, но затем решил, что это не такая хорошая идея.

Я исследовал это по моему запросу выше и 4000 записей.

Быстрый анализ:

eager_load принял 2,600 миллисекунды. включает в себя 72 миллисекунды.

eager_load занимает в среднем 36 раз.