Резервное копирование БД запросов и передовой практики
Загрузка БД на мой сайт становится очень высокой, поэтому мне пора кэшировать общие запросы, которые называются 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.find(1), а @user.city.name, где Rails не использует выборку, но снова запрашивает DB... что имеет смысл, но не совсем то, что я хочу.
Я могу сделать City.find(@user.city_id), но это уродливо.
Итак, мой вопрос к вам, ребята. Что делают умные люди? Что
правильный способ сделать это?
Ответы
Ответ 1
Что касается кэширования, то несколько второстепенных моментов:
Стоит использовать слэш для разделения типа объекта и id, что является условным обозначением rails. Более того, модели ActiveRecord предоставляют метод экземпляра cacke_key, который будет предоставлять уникальный идентификатор имени и идентификатора таблицы, "городов/13" и т.д.
Небольшая поправка к вашему фильтру after_save. Поскольку у вас есть данные, вы можете записать их обратно в кеш, а не удалять их. Это экономит вам одну поездку в базу данных;)
def after_save
Rails.cache.write(cache_key,self)
end
Что касается корня вопроса, если вы постоянно тянете @user.city.name, есть два реальных варианта:
- Денормализовать имя города пользователя в строке пользователя. @user.city_name (сохранить внешний ключ city_id). Это значение должно быть записано в течение сэкономленного времени.
-or-
- Внедрите свой метод User.fetch, чтобы загружать город. Делайте это только в том случае, если содержимое строки города никогда не изменяется (например, имя и т.д.), Иначе вы можете открыть банку червей в отношении недействительности кеша.
Личное мнение:
Реализовать базовые методы выборки на основе id (или использовать плагин) для интеграции с memcached и денормализовать имя города в строке пользователя.
Я лично не большой поклонник кэшированных плагинов модельного стиля, я никогда не видел того, что спасло значительное время разработки, из-за которого я не спешил.
Если вы получаете слишком много запросов к базе данных, то определенно стоит проверить загрузку (через: include), если вы еще этого не сделали. Это должен быть первый шаг для уменьшения количества запросов к базе данных.
Ответ 2
Я бы пошел и посмотрю Memoization, который теперь находится в Rails 2.2.
"Воспоминание - это образец инициализация метода один раз, а затем отбросив его значение для повторения использовать".
В последнее время появился отличный эпизод 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
Подробнее о Memoization
Ответ 3
Если вам нужно ускорить sql-запросы на данные, которые со временем сильно меняются, вы можете использовать материализованные представления.
Matview сохраняет результаты запроса в табличной структуре свой собственный, из которого данные могут быть запрошены. Невозможно добавить или удалять строки, но в остальное время он ведет себя так же, как фактическая таблица. Запросы быстрее, и сам matview может быть индексируются.
На момент написания статьи, matviews изначально доступны в Oracle DB, PostgreSQL, Sybase, IBM DB2 и Microsoft SQL Server. MySQL к сожалению, не предоставляет встроенную поддержку matviews, но там являются альтернативой с открытым исходным кодом.
Вот несколько хороших статей о том, как использовать matviews в Rails
sitepoint.com/speed-up-with-materialized-views-on-postgresql-and-rails
hashrocket.com/materialized-view-strategies-using-postgresql
Ответ 4
Отъезд cached_model