Загрузка БД на моем сайте становится очень высокой, поэтому мне пора кэшировать общие запросы, которые вызываются 1000 раз в час, когда результаты не меняются. Так, например, на моей модели города я делаю следующее:
def self.fetch(id)
Rails.cache.fetch("city_#{id}") { City.find(id) }
end
def after_save
Rails.cache.delete("city_#{self.id}")
end
def after_destroy
Rails.cache.delete("city_#{self.id}")
end
Итак, теперь, когда я могу City.find (1) в первый раз, я попадаю в БД, но следующие 1000 раз я получаю результат из памяти. Большой. Но большинство вызовов city не City.find (1), а @ user.city.name, где Rails не использует выборку, а снова запрашивает БД ... что имеет смысл, но не совсем то, что я хочу.
Я могу использовать City.find (@ user.city_id), но это некрасиво.
Итак, мой вопрос к вам, ребята. Что делают умные люди? Что такое правильный способ сделать это?





Проверить cached_model
Мемоизация не оборачивает кеш Rails. Rails.cache обычно является хранилищем кэша, совместно используемым между процессами (таким образом, вы фактически получаете преимущества кэширования). Мемоизация происходит только в текущем процессе.
Я бы пошел дальше и взглянул на Мемоизация, который сейчас находится в Rails 2.2.
"Memoization is a pattern of initializing a method once and then stashing its value away for repeat use."
Недавно на нем был замечательный Эпизод Railscast, который должен заставить вас нормально работать.
Быстрый пример кода из Railscast:
class Product < ActiveRecord::Base
extend ActiveSupport::Memoizable
belongs_to :category
def filesize(num = 1)
# some expensive operation
sleep 2
12345789 * num
end
memoize :filesize
end
В чем преимущество мемоизации перед Rails.cache и как это решает проблему User.city.name, когда Rails находит в City вместо использования кэшированных данных?
Мемоизация будет сохранять результат только для каждого серверного процесса и не делает его недействительным, поскольку он относительно не подходит для вашей ситуации и не предназначен для использования таким образом.
По поводу кеширования пара мелких моментов:
Для разделения типа объекта и идентификатора стоит использовать косую черту, что является условным обозначением рельсов. Более того, модели ActiveRecord предоставляют метод экземпляра cacke_key, который предоставляет уникальный идентификатор, состоящий из имени и идентификатора таблицы, «городов / 13» и т. д.
Одно небольшое исправление в вашем фильтре after_save. Поскольку у вас есть данные под рукой, вы можете записать их обратно в кеш, а не удалять. Это сэкономит вам одну поездку в базу данных;)
def after_save Rails.cache.write(cache_key,self) end
Что касается корня вопроса, если вы постоянно нажимаете @ user.city.name, есть два реальных варианта:
-или же-
Личное мнение: Реализуйте базовые методы выборки на основе идентификатора (или используйте плагин) для интеграции с memcached и денормализуйте название города в строке пользователя.
Я лично не большой поклонник плагинов в стиле кэшированных моделей, я никогда не видел таких, которые позволяли бы сэкономить значительное количество времени на разработку, из-за чего я не вырос в спешке.
Если вы получаете слишком много запросов к базе данных, определенно стоит проверить активную загрузку (через: include), если вы еще этого не сделали. Это должно быть первым шагом к уменьшению количества запросов к базе данных.
Например, User.where(id: 3).includes(:city).first. join-merge недооценивается во взаимоотношениях "многие-многие-многие". EG City.joins(:restaurants).merge(user.favorite_restaurants), вернет все города, в которых у пользователя много любимых ресторанов, а у ресторанов много городов. Проверьте включает и присоединяется. Определенно стоит узнать, как сократить количество вызовов с помощью мощных запросов, вместо того, чтобы мучиться с кешированием.
Если вам нужно ускорить sql-запросы к данным, которые не сильно меняются со временем, вы можете использовать материализованные представления.
A matview stores the results of a query into a table-like structure of its own, from which the data can be queried. It is not possible to add or delete rows, but the rest of the time it behaves just like an actual table. Queries are faster, and the matview itself can be indexed.
At the time of this writing, matviews are natively available in Oracle DB, PostgreSQL, Sybase, IBM DB2, and Microsoft SQL Server. MySQL doesn’t provide native support for matviews, unfortunately, but there are open source alternatives to it.
Вот несколько хороших статей о том, как использовать matviews в Rails.
sitepoint.com/speed-up-with-materialized-views-on-postgresql-and-rails
hashrocket.com/materialized-view-strategies-using-postgresql
мемоизация просто обертывает Rails.cache. Я не думаю, что это поможет модельным ассоциациям, которые вы ищете.