#jquery #ruby-on-rails #performance
#jquery #ruby-on-rails #Производительность
Вопрос:
У меня есть модель продуктов с 6000 записями в ней (не так много). Когда я запускаю цикл в моем представлении, чтобы просто перечислить все записи в таблице, аналогично этому..
<% Product.all.each do |product| %>
<%= product.serial_number %> <br />
<% end %>
Для загрузки в моем представлении и отображения моих записей на веб-странице требуется 13,02 секунды. Это есть как в моем dev ENV, так и в prod ENV, и я пытался использовать базы данных MySQL и sqlite. Кажется, нет никакой разницы.
НО, когда я запускаю этот запрос в консоли rails, он завершается за 88,2 мс (менее 1 секунды).
Что может быть причиной этого?
Комментарии:
1. Есть ли у вас ассоциации с этой моделью?
2. Да, на самом деле у меня их около 8. 6 из них являются belongs_to: и 2 из них являются ассоциациями has_many:. И теперь, когда вы заговорили об этом, я вижу, что с ними связано множество выполняемых SQL. Ищет ли он каждую ассоциацию для каждой строки?
3. Да, это так. Смотрите мой подробный ответ ниже
Ответ №1:
Объединение
Самое главное, вы должны присоединиться к ассоциациям, к которым можно присоединиться. Загляните в журнал вашего сервера и посмотрите, когда вы отобразите страницу, есть ли у вас длинный список выборки из базы данных. Например, если ваш Product
связан с User
, вы увидите один «SELECT» для вашего списка продуктов, а затем сотню «SELECT» для ваших пользователей. Возможно, вы захотите использовать этот синтаксис
<% Products.joins(:user).each do |product| %>
<%= product.serial_number %> <br />
<% end %>
Обратите внимание, что вы должны адаптироваться к своему коду (я использовал user
в качестве примера), все зависит от ваших ассоциаций. Смотрите больше в руководствах здесь
Обратите внимание, что joins
выполните «ВНУТРЕННЕЕ ОБЪЕДИНЕНИЕ» и удалите все записи, которые имеют нулевую ссылку. Если это ваш случай, вы можете использовать метод «Нетерпеливой загрузки», используя includes
:
Products.includes(:user).each do |product|
Find_each
При желании попробуйте использовать find_each
который будет обрабатывать ваши записи пакетами, чтобы сэкономить мощность сервера. Смотрите более подробную информацию об этом в руководствах
Products.find_each do |product|
Кэширование
При желании вы можете использовать кэширование для повышения производительности
Products.find_each do |product|
cache product do
...
end
end
Разбивка на страницы
А также рассмотрите возможность использования разбивки на страницы, если это возможно, чтобы ограничить количество записей, извлекаемых из базы данных
Комментарии:
1. Спасибо Бенджамину. Когда я добавляю теги <% cache product %> и <% end %>, я получаю следующую ошибку: синтаксическая ошибка, неожиданный keyword_ensure, ожидание завершения ввода
2. Мой плохой, я забыл
do
(отредактировал свой ответ). В любом случае, я думаю, вы можете напрямую перейти к главе «присоединение», потому что, согласно вашему последнему комментарию, это, по-видимому, основная причина вашего медленного запроса3. Спасибо за ясность. При добавлении всех столбцов для отображения мой запрос занял около 32 секунд в представлении. Добавление этого улучшилось примерно до 11, что здорово. Но много ли это времени для 6000 записей? Я всегда думал, что rails достаточно мощный, чтобы загрузить эти несколько записей максимум за пару секунд.
4. Трудно сказать, многие элементы могут влиять на скорость, такие как правильная индексация записей вашей базы данных, скорость вашего серверного кэширования и т.д… Google должен предоставить некоторую полезную информацию для вашего случая
Ответ №2:
Вам нужно одно поле? Используйте pluck
. Это именно то, для чего это нужно.
<% Products.pluck(:serial_number).each do |serial_number| %>
<%= serial_number %> <br />
<% end %>
Это одновременно генерирует более эффективный SQL ( select serial_number
вместо select *
) и обходит этап создания экземпляров объектов ActiveRecord для каждого возвращаемого результата, только чтобы вы могли получить доступ к одному свойству этой модели.
НО, когда я запускаю этот запрос в консоли rails, он завершается за 88,2 мс (менее 1 секунды).
Если вы на самом деле не начнете перебирать результаты, Rails ничего не сделает. Rails фактически создает экземпляры записей только при попытке доступа к результатам.
Re: Ваш комментарий
Однако на самом деле я просто показывал выше в качестве примера. На самом деле я отображаю все поля для каждой записи. Какие-либо хитрости для этого?
Тогда вы не должны использовать цикл, как показывает ваш пример. Используйте частичные элементы и рендеринг коллекции, что намного быстрее, чем повторять результаты самостоятельно.
Создайте представление с именем app/views/products/_product.html.erb
и поместите «тело» цикла в этот файл. Это может выглядеть примерно так:
<div class="product">
Name: <%= link_to product, product.name %><br/>
Serial Number: <%= product.serial_number %>
</div>
Затем в представлении верхнего уровня отобразите всю коллекцию, используя этот параметр:
render partial: 'product', collection: Product.all
Комментарии:
1. Я получаю это: неопределенная локальная переменная или метод `product’ для #<#<Класс: 0x00000110099668>:0x00000109060d10>
2. Зависит от того, как называется ваша модель.
3. Это модель продуктов, хммм
4. У вас никогда не должно быть модели с именем «Продукты». Это должен быть «Продукт». Это чрезвычайно важное утверждение в Rails, и вы должны придерживаться его, если у вас нет очень веских причин не делать этого.
5. Извините, это действительно модель Product.rb