Кэширование необходимо для повышения производительности и масштабируемости веб-приложений, и кэширование в Ruby on Rails не является исключением. Сохраняя и повторно используя результаты дорогостоящих вычислений или запросов к базе данных, кэширование значительно сокращает время и ресурсы, необходимые для обслуживания запросов пользователей.

Здесь мы рассмотрим, как реализовать различные типы кэширования в Rails, такие как кэширование фрагментов и кэширование матрешки. Мы также покажем вам, как управлять зависимостями кэша и выбирать хранилища кэша, а также изложим рекомендации по эффективному использованию кэширования в приложении Rails.

В этой статье предполагается, что вы знакомы с Ruby on Rails, используете Rails версии 6 или выше и чувствуете себя комфортно, используя представления Rails. В примерах кода показано, как использовать кэширование внутри новых или существующих шаблонов представлений.

Типы кэширования Ruby on Rails

В приложениях Ruby on Rails доступно несколько типов кэширования, в зависимости от уровня и детализации кэшируемого контента. Основные типы, используемые в современных приложениях Rails:

  • Кэширование фрагментов: кэширует редко изменяющиеся части веб-страницы, такие как верхние и нижние колонтитулы, боковые панели или статическое содержимое. Кэширование фрагментов уменьшает количество частей или компонентов, отображаемых при каждом запросе.
  • Кэширование русской куклы: кэширует вложенные фрагменты веб-страницы, которые зависят друг от друга, например коллекции и ассоциации. Кэширование матрешки предотвращает ненужные запросы к базе данных и упрощает повторное использование неизмененных кэшированных фрагментов.

Два дополнительных типа кэширования ранее были частью Ruby on Rails, но теперь доступны как отдельные драгоценные камни:

  • Кэширование страниц: кэширует целые веб-страницы как статические файлы на сервере, минуя весь жизненный цикл рендеринга страницы.
  • Кэширование действий: Кэширует вывод всех действий контроллера. Это похоже на кэширование страниц, но позволяет применять фильтры, такие как аутентификация.

Кэширование страниц и действий редко используется и больше не рекомендуется для большинства случаев использования в современных приложениях Rails.

Кэширование фрагментов в Ruby on Rails

Кэширование фрагментов позволяет кэшировать редко изменяющиеся части страницы. Например, страница, отображающая список продуктов с соответствующими ценами и рейтингами, может кэшировать детали, которые вряд ли изменятся.

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

Как самый простой тип кэширования, включенный в Rails, кэширование фрагментов должно быть вашим первым выбором при добавлении кэширования в ваше приложение для повышения производительности.

Чтобы использовать кэширование фрагментов в Rails, используйте cache вспомогательный метод в ваших представлениях. Например, напишите следующий код для кэширования части продукта в вашем представлении:

<% @products.each do |product| %>
  <% cache product do %>
    <%= render partial: "product", locals: { product: product } %>
  <% end %>
<% end %>

cache помощник генерирует ключ кэша на основе имени класса каждого элемента, idи updated_at метка времени (например, products/1-20230501000000). В следующий раз, когда пользователь запрашивает тот же продукт, cache helper извлечет закэшированный фрагмент из хранилища кеша и отобразит его, не считывая продукт из базы данных.

Вы также можете настроить ключ кэша, передав параметры в cache помощник. Например, чтобы включить номер версии или отметку времени в ключ кэша, напишите что-то вроде этого:

<% @products.each do |product| %>
  <% cache [product, "v1"] do %>
    <%= render partial: "product", locals: { product: product } %>
  <% end %>
<% end %>

Кроме того, вы можете установить срок действия:

<% @products.each do |product| %>
  <% cache product, expires_in: 1.hour do %>
    <%= render partial: "product", locals: { product: product } %>
  <% end %>
<% end %>

Первый пример добавит v1 к ключу кеша (например, products/1-v1). Это полезно для аннулирования кеша при изменении частичного шаблона или макета. Во втором примере задается срок действия записи в кэше (1 час), что помогает удалить устаревшие данные.

Кэширование русской куклы в Ruby on Rails

Кэширование матрешки — это мощная стратегия кэширования в Ruby on Rails, которая оптимизирует производительность вашего приложения за счет вложения кэшей друг в друга. Он использует кэширование фрагментов Rails и зависимости кэша, чтобы свести к минимуму избыточную работу и сократить время загрузки.

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

Недостатком кэширования Russian Doll является сложность. Вы должны понимать отношения между вложенными уровнями элементов, которые вы кэшируете, чтобы убедиться, что вы кэшируете правильные элементы. В некоторых случаях вам потребуется добавить ассоциации к вашим моделям Active Record, чтобы Rails мог вывести отношения между кэшированными элементами данных.

Как и при обычном кэшировании фрагментов, кэширование матрешки использует cache вспомогательный метод. Например, чтобы кэшировать категорию с ее подкатегориями и продуктами в вашем представлении, напишите что-то вроде этого:

<% @categories.each do |category| %>
  <% cache category do %>
    <h2><%= category.name %></h2>
    <% category.subcategories.each do |subcategory| %>
    <% cache subcategory do %>
    <h3><%= subcategory.name %></h3>
    <% subcategory.products.each do |product| %>
    <% cache product do %>
        <%= render partial: "product", locals: { product: product } %>
        <% end %>
    <% end %>
    <% end %>
    <% end %>
  <% end %>
<% end %>

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

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

Управление зависимостями кэша в Ruby on Rails

Зависимости кэша — это отношения между кэшированными данными и их базовыми источниками, и управлять ими может быть сложно. Если исходные данные изменяются, любые связанные кэшированные данные должны истечь.

Rails может использовать метки времени для автоматического управления большинством зависимостей кэша. Каждая модель Active Record имеет created_at и updated_at атрибуты, указывающие, когда кэш создал или в последний раз обновил запись. Чтобы Rails мог автоматически управлять кэшированием, определите отношения ваших моделей Active Record следующим образом:

class Product < ApplicationRecord
  belongs_to :category
end
class Category < ApplicationRecord
  has_many :products
end

В этом примере:

  • Если вы обновите запись о продукте (например, изменив его цену), его updated_at метка времени меняется автоматически.
  • Если вы используете эту метку времени как часть ключа кэша (например, products/1-20230504000000), он также автоматически делает ваш кешированный фрагмент недействительным.
  • Чтобы аннулировать кешированный фрагмент вашей категории при обновлении записи о продукте — возможно, потому, что он показывает некоторые агрегированные данные, такие как средняя цена, — используйте touch метод в вашем контроллере (@product.category.touch) или добавить touch вариант в вашей ассоциации модели (belongs_to :category touch: true).

Другой механизм управления зависимостями кэша — использование низкоуровневых методов кэширования, таких как fetch и write — прямо в ваших моделях или контроллерах. Эти методы позволяют хранить произвольные данные или контент в хранилище кеша с помощью настраиваемых ключей и параметров. Например:

class Product < ApplicationRecord
  def self.average_price
    Rails.cache.fetch("products/average_price", expires_in: 1.hour) do
    average(:price)
    end
  end
end

В этом примере показано, как кэшировать рассчитанные данные, такие как средняя цена всех продуктов, за час с помощью fetch метод с пользовательским ключом (products/average_price) и опцион истечения (expires_in: 1.hour).

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

Чтобы вручную аннулировать запись кэша до истечения срока ее действия, используйте команду write метод с force вариант:

Rails.cache.write("products/average_price", Product.average(:price), force: true))

Хранилища кэша и серверные части в Ruby on Rails

Rails позволяет вам выбирать различные хранилища кеша или серверные части для хранения кэшированных данных и контента. Кэш-хранилище Rails — это уровень абстракции, обеспечивающий общий интерфейс для взаимодействия с различными системами хранения. Серверная часть кэша реализует интерфейс хранилища кэша для конкретной системы хранения.

Rails поддерживает несколько типов хранилищ кеша или бэкэндов из коробки, которые подробно описаны ниже.

Хранилище памяти

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

Дисковый магазин

Дисковое хранилище использует файлы на диске в качестве кэш-хранилища. Это самый медленный вариант кэширования в Rails, но он обладает большой емкостью и устойчивостью. Дисковое хранилище подходит для приложений, которые должны кэшировать большие объемы данных и не нуждаются в максимальной производительности.

Редис

Магазин Redis использует экземпляр Redis для хранения кэша. Redis — это хранилище данных в памяти, которое поддерживает несколько типов данных. Хотя это быстро и гибко, для этого требуется отдельный сервер и конфигурация. Он подходит для приложений, которые должны кэшировать сложные или динамические данные, которые часто изменяются. Redis — идеальный выбор при запуске приложений Rails в облаке, поскольку некоторые хостинг-провайдеры, в том числе Kinsta, предлагают Redis в качестве постоянного кеша объектов.

Memcached

Магазин Memcached использует экземпляр Memcached для хранения кеша. Memcached — это хранилище ключей и значений в памяти, которое поддерживает простые типы данных и функции. Он быстрый и масштабируемый, но, как и Redis, требует отдельного сервера и конфигурации. Это хранилище подходит для приложений, которым необходимо кэшировать простые или статические данные, которые часто обновляются.

Вы можете настроить хранилище кеша в файлах среды Rails (например, конфиг/окружения/развитие.rb) используя config.cache_store вариант. Вот как использовать каждый из встроенных методов кэширования Rails:

# Use memory store
config.cache_store = :memory_store
# Use disk store
config.cache_store = :file_store, "tmp/cache"
# Use Redis
config.cache_store = :redis_cache_store, { url: "redis://localhost:6379/0" }
# Use Memcached
config.cache_store = :mem_cache_store, "localhost"

У вас должен быть только один config.cache_store вызов для каждого файла среды. Если у вас их несколько, хранилище кеша использует только последний.

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

Лучшие практики кэширования Ruby on Rails

Использование кэширования в вашем приложении Rails может значительно повысить его производительность и масштабируемость, особенно если вы реализуете следующие рекомендации:

  • Выборочное кэширование: Кэшировать только часто используемые, дорогостоящие или редко обновляемые данные. Избегайте чрезмерного кэширования, чтобы предотвратить чрезмерное использование памяти, риски устаревших данных и снижение производительности.
  • Срок действия записей кэша: Предотвращение устаревания данных путем удаления недействительных или нерелевантных записей. Используйте временные метки, варианты истечения срока действия или ручное аннулирование.
  • Оптимизация производительности кэша: выберите хранилище кеша, соответствующее потребностям вашего приложения, и точно настройте его параметры — например, размер, сжатие или сериализацию — для оптимальной производительности.
  • Отслеживайте и тестируйте влияние кэша: Оцените поведение кэша — например, частоту попаданий, частоту промахов и задержку — и оцените их соответствующее влияние на производительность (время отклика, пропускную способность, использование ресурсов). Используйте такие инструменты, как New Relic, журналы Rails, уведомления ActiveSupport или мини-профилировщик Rack.

Краткое содержание

Кэширование Ruby on Rails повышает производительность и масштабируемость приложений за счет эффективного хранения и повторного использования часто используемых данных или контента. Благодаря более глубокому пониманию методов кэширования вы лучше подготовлены для более быстрой доставки приложений Rails своим пользователям.

При развертывании вашего оптимизированного приложения Rails вы можете обратиться к платформе хостинга приложений Kinsta. Начните бесплатно с учетной записью Hobby Tier и изучите платформу с помощью этого краткого примера Ruby on Rails.