Почему области Rails предпочтительнее, если грязные контроллеры работают быстрее? - PullRequest
4 голосов
/ 30 ноября 2010

Я пытался связать запросы Arel с помощью областей, а не просто с использованием какой-то длинной логики, которую я написал в контроллере. Но области видимости медленнее, чем просто получить все записи и затем проанализировать их с некоторой логикой. Тогда мне интересно, почему области лучше.

Вот что я делаю:

  • вопрос имеет много ответов
  • ответ принадлежит одному вопросу
  • вопрос имеет столбец «question_type», который я использую для его сортировки

Во-первых, путь видимости ...

в вопросе. Rb:

scope :answered, joins(:answers).order('answers.created_at desc')
scope :dogs, where(:question_type => "dogs")
scope :cats, where(:question_type => "cats")
scope :mermaids, where(:question_type => "mermaids")

in questions_controller.rb:

@dogs_recently_answered = Question.answered.dogs.uniq[0..9]
@cats_recently_answered = Question.answered.cats.uniq[0..9]
@mermaids_recently_answered = Question.answered.mermaids.uniq[0..9]

Затем в представлении я перебираю переменные экземпляра (которые теперь являются массивами, содержащими не более 10 элементов) и отображаю результаты.

Время загрузки страницы (пять раз):

Выполнено 200 OK за 535 мс (Просмотры: 189,6 мс | ActiveRecord: 46,2 мс)

Выполнено 200 OK за 573 мс (Просмотры: 186,0 мс | ActiveRecord: 46,3 мс)

Выполнено 200 OK за 577 мс (Просмотров: 189,0 мс | ActiveRecord: 45,6 мс)

Выполнено 200 OK за 532 мс (Просмотров: 182,9 мс | ActiveRecord: 46 мс)

Выполнено 200 OK за 577 мс (Просмотров: 186,7 мс | ActiveRecord: 46,9 мс)

Теперь, грязный контроллер ...

@answers = Answer.order("created_at desc")
@all_answered = []
@answers.each {|answer| @all_answered << answer.question}
@recently_answered = @all_answered.uniq
@dogs_all_answered = []
@cats_all_answered = []
@mermaids_all_answered = []
@recently_answered.each do |q|
  if q.question_type == "dogs"
    @dogs_all_answered << q
    @dogs_recently_answered = @dogs_all_answered[0..9]
  elsif q.question_type == "cats"
    @cats_all_answered << q
    @cats_recently_answered = @cats_all_answered[0..9]
  elsif q.question_type == "mermaids"
    @mermaids_all_answered << q
    @mermaids_recently_answered = @mermaids_all_answered[0..9]
  end
end

А вот время загрузки страницы (пять раз):

Выполнено 200 OK за 475 мс (Просмотров: 196,5 мс | ActiveRecord: 34,5 мс)

Выполнено 200 OK за 480 мс (Просмотры: 200,4 мс | ActiveRecord: 36,4 мс)

Выполнено 200 OK за 434 мс (Просмотры: 198,2 мс | ActiveRecord: 35,8 мс)

Выполнено 200 OK за 475 мс (Просмотров: 194,2 мс | ActiveRecord: 36,4 мс)

Завершено 200 OK за 475 мс (Просмотры: 195,0 мс | ActiveRecord: 35,4 мс)

Итак ...

Помимо читабельности, что можно выиграть, оттачивая запрос с областью? Становится ли это быстрее, когда появляется больше записей?

Ответы [ 2 ]

4 голосов
/ 30 ноября 2010

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

Вот как я бы подошел к этому:

scope :answered, joins(:answers).order('answers.created_at desc')
scope :recent, take(10)
scope :dogs, where(:question_type => "dogs")
scope :cats, where(:question_type => "cats")
scope :mermaids, where(:question_type => "mermaids")

@dogs_recently_answered = Question.answered.dogs.recent
@cats_recently_answered = Question.answered.dogs.recent
@mermaids_recently_answered = Question.answered.dogs.recent

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

scope :unique, select('DISTINCT column_name')

, а затем вы можете использовать Question.cats.unique.recent и получить все это в одном быстром запросе, который использует реляционную алгебру, для которой предназначены системы баз данных.

1 голос
/ 30 ноября 2010

Я думаю, что причина в том, что области в этом случае медленнее, потому что они приводят к 3 отдельным запросам к базе данных, тогда как другой подход использует знание того, что все три результата могут быть удовлетворены одним используемым запросом.

Предполагая, что это так, неудивительно, что области выполняют 3 отдельных запроса, поскольку система не знает, когда вы вызываете первый, который вы собираетесь вызывать сразу после этого. Возможно, есть стратегия оптимизации, которая была бы разумна для этого сценария, но я не знаю, что ActiveRecord реализует ее.

В любом случае, это один из недостатков области применения в данном конкретном случае. Мне нравятся области видимости, потому что они чистые / понятные, гибкие и содержат именованную абстракцию для запроса. AFAICT, во многих случаях они не заметно медленнее, чем эквивалентный прямой запрос.

...