Стратегия кэширования

«В информатике есть только две трудные вещи: аннулирование кэша и именование вещей». — Фил Карлтон

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

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

  • Наименее часто используемая (LFU): Эта стратегия использует счетчик для отслеживания частоты обращения к записи, и элемент с наименьшим счетчиком удаляется первым.
  • Наименее часто используемый (LRU): В этом случае недавно использованные элементы всегда находятся в верхней части кэша, а когда нам нужно освободить место, элементы, к которым недавно не было обращений, удаляются.
  • Most Recently Used (MRU): Недавно использованные элементы удаляются первыми. Мы будем использовать этот подход в ситуациях, когда чаще всего обращаются к более старым элементам.

Самое время задуматься о стратегии кэширования, когда вы разрабатываете каждый модуль, необходимый для вашего приложения.
Каждый раз, когда ваш модуль возвращает данные, вы должны задать себе несколько вопросов:
Возвращаем ли мы разумные данные, которые не можем хранить в каком-либо месте?
Возвращаем ли мы тот же результат, если входные данные останутся прежними?
Как долго мы можем хранить эти данные?
Как мы хотим аннулировать этот кэш?
Вы можете добавить слой кэша в любое место вашего приложения.
Например, если вы используете MySQL/MariaDB в качестве хранилища данных, вы можете включить и правильно настроить кэш запросов.
Эта небольшая настройка даст вашей базе данных толчок к развитию.
О кэше нужно думать и во время кодирования.
Вы можете сделать ленивую загрузку объектов и данных или создать собственный слой кэша для повышения общей производительности.
Представьте, что вы запрашиваете и обрабатываете данные из внешнего хранилища, причем запрашиваемые данные могут повторяться несколько раз за одно выполнение.
Если сделать что-то похожее на следующий фрагмент кода, это уменьшит количество обращений к внешнему хранилищу:

<?php

class Cache
{
    protected $cache = [];
    public function getData($id)
    {
        if (empty($this->cache[$id])) {
            $externalData = $this->getExternalData($id);
            if ($externalData !== false) {
                $this->cache[$id] = $externalData;
            }
        }
        return $this->cache[$id];
    }
}
Войти в полноэкранный режим Выход из полноэкранного режима

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

В PHP у вас есть доступ к самым популярным кэш-серверам, таким как memcached и Redis, которые хранят свои данные в формате ключ-значение.
Наличие доступа к этим мощным инструментам позволит нам увеличить производительность наших приложений.

Давайте перестроим наш предыдущий пример, используя Redis в качестве кэш-хранилища.
В следующем фрагменте кода мы будем считать, что в вашей среде доступна библиотека Redis (например, phpredis) и запущен сервер Redis:

<?php
 class Cache
{
    protected $cache = null;
    public function __construct()
    {
        $this->cache = new Redis();
        $this->cache->connect('127.0.0.1', 6379);
    }
    public function getData($id)
    {
        $externalData = $this->cache->get($id);
        if ($externalData === false) {
            $externalData = $this->getExternalData($id);
            if ($externalData !== false) {
                $this->cache->set($id, $externalData);
            }
        }
        return $externalData;
    }
}
Перейдите в полноэкранный режим Выход из полноэкранного режима

Здесь мы сначала подключились к серверу Redis и адаптировали функцию getData для использования нашего нового экземпляра Redis.
Этот пример можно усложнить, например, добавив инъекцию зависимости и хранение JSON в кэше, среди прочих бесконечных вариантов.
Одним из преимуществ использования кэш-движка вместо создания собственного является то, что все они поставляются с множеством классных и полезных функций.
Представьте, что вы хотите хранить данные в кэше всего 10 секунд. Это очень легко сделать с Redis, просто измените вызов set на $this->cache->set($id, $externalData, 10) и через десять секунд ваша запись будет стерта из кэша.
Еще более важным моментом, чем добавление данных в кэш, является аннулирование или удаление сохраненных данных.
В некоторых случаях можно использовать старые данные, но в других случаях использование старых данных может привести к проблемам.
Если вы не добавляете TTL для автоматического истечения срока хранения данных, убедитесь, что у вас есть способ удаления или аннулирования данных, когда это необходимо.
Как разработчик, вы не должны быть привязаны к конкретному механизму кэширования. Оберните его, создайте абстракцию и используйте эту абстракцию, чтобы в любой момент можно было изменить базовый механизм без изменения всего кода.

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

  • Expires: Устанавливает время в будущем, когда срок действия содержимого истечет. Когда это время будет достигнуто, все подобные запросы должны будут вернуться на исходный сервер.
  • Last-modified: Этот параметр определяет время последнего изменения ответа. Он может быть использован как часть вашей пользовательской стратегии проверки, чтобы гарантировать, что ваши пользователи всегда будут иметь свежий контент.
  • Etag: Этот тег заголовка является одним из нескольких механизмов, которые HTTP предоставляет для проверки веб-кэша, что позволяет клиенту делать условные запросы. Etag — это идентификатор, присваиваемый сервером определенной версии ресурса. Если ресурс изменяется, Etag также изменяется, что позволяет нам быстро сравнить два представления ресурса, чтобы определить, являются ли они одинаковыми.
  • Pragma: Это старый заголовок, оставшийся от реализации HTTP/1.0. HTTP/1.1 Cache-control реализует ту же концепцию.
  • Cache-control: Этот заголовок заменяет заголовок expires. Он хорошо поддерживается и позволяет нам реализовать более гибкую стратегию кэширования. Различные значения этого заголовка можно комбинировать для достижения различных моделей кэширования.

Ниже перечислены доступные варианты:

  • no-cache: Это означает, что любое кэшированное содержимое должно проверяться при каждом запросе перед отправкой клиенту.
  • no-store: Это означает, что содержимое не может быть кэшировано каким-либо образом. Этот параметр полезен, когда ответ содержит конфиденциальные данные.
  • public: Помечает содержимое как общедоступное, и оно может кэшироваться браузером и любыми промежуточными кэшами.
  • private: Помечает содержимое как приватное. Это содержимое может быть сохранено браузером пользователя, но не промежуточными сторонами.
  • max-age: Устанавливает максимальный срок, в течение которого содержимое может находиться в кэше, прежде чем его необходимо будет перепроверить. Значение этой опции измеряется в секундах, максимальное значение — 1 год (31 536 000 секунд).
  • s-maxage: Эта опция аналогична заголовку max-age. разница лишь в том, что эта опция применяется только к промежуточным кэшам.
  • must-revalidate: Этот тег указывает, что правила, указанные в max-age, s-maxage. или заголовке expires, должны строго соблюдаться.
  • proxy-revalidate: Этот параметр аналогичен s-maxage, но применяется только к промежуточным прокси-серверам.
  • no-transform: Этот заголовок сообщает кэшам, что им не разрешается изменять полученное содержимое ни при каких обстоятельствах.

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

  • Логотипы и неавтогенерируемые изображения
  • таблицы стилей
  • файлы JavaScript
  • Загружаемый контент
  • Любые медиафайлы

Эти элементы обычно меняются нечасто, поэтому их можно кэшировать в течение длительного времени.
Чтобы снизить нагрузку на серверы, можно использовать сеть доставки контента (CDN), чтобы эти редко изменяемые файлы обслуживались внешними серверами.
В принципе, существует два типа CDN:

1 — Push CDN: Этот тип требует от вас проталкивания файлов, которые вы хотите хранить.
Вы несете ответственность за то, что вы загружаете правильный файл в CDN, и что загруженный ресурс доступен.
Этот тип в основном используется для загруженных изображений, например
например, аватара вашего пользователя. Обратите внимание, что некоторые CDN могут вернуть ответ OK после push, но ваш файл еще не готов.

2 — CDN Pull: Это ленивая версия, вам не нужно ничего отправлять в CDN.
Когда через CDN приходит запрос, а файла нет в их хранилище, они получают ресурс с вашего сервера и сохраняют его для будущих запросов.
Этот метод в основном используется для CSS, изображений и JavaScript.

Некоторые из известных CDN — CloudFlare, Amazon CloudFront, Fastly и другие.
Их объединяет то, что они имеют несколько центров обработки данных по всему миру, что позволяет им пытаться предоставить вам копию вашего файла с ближайшего сервера.
Комбинируя HTTP со стратегиями кэширования статических файлов, вы сведете запросы к активам на вашем сервере к минимуму. Мы не будем объяснять другие стратегии кэширования, такие как полностраничное
С тем, что мы рассмотрели, вам достаточно, чтобы начать строить успешное приложение.

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *