Почему Rails Scopes предпочтительнее, если грязные контроллеры быстрее?

StackOverflow https://stackoverflow.com/questions/4314273

Вопрос

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

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

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

Во -первых, The Scopes Way ...

в вопросе. 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")

в вопросах_контроллере.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 ОК за 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,1 мс)

Завершено 200 ОК в 577 мс (просмотры: 186,7 мс | Activerecord: 46.9ms)

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

@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 ОК за 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 мс)

Так...

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

Это было полезно?

Решение

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

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

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')

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

Другие советы

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top