Кэширование данных

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

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

// try retrieving $data from cache
$data = $cache->get($key);

if ($data === false) {
    // $data is not found in cache, calculate it from scratch
    $data = $this->calculateSomething();

    // store $data in cache so that it can be retrieved next time
    $cache->set($key, $data);
}

// $data is available here

Компонент cache предоставляет метод getOrSet(), который упрощает код для получения, вычисления и хранения данных. Следующий код делает то же самое, что и в предыдущем примере:

$data = $cache->getOrSet($key, function () {
    return $this->calculateSomething();
});

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

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

$user_id = 42;
$data = $cache->getOrSet($key, function () use ($user_id) {
    return $this->calculateSomething($user_id);
});

Компоненты кэша

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

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

'components' => [
    'cache' => [
        'class' => 'yii\caching\MemCache',
        'servers' => [
            [
                'host' => 'server1',
                'port' => 11211,
                'weight' => 100,
            ],
            [
                'host' => 'server2',
                'port' => 11211,
                'weight' => 50,
            ],
        ],
    ],
],

Затем вы можете получить доступ к вышеупомянутому компоненту кэша, используя выражение Yii::$app->cache.

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

'components' => [
    'cache' => [
        'class' => 'yii\caching\ApcCache',
    ],
],

Поддерживаемое кэширование

Yii поддерживает широкий диапазон кэширования. Ниже приводится резюме:

  • yii\caching\ApcCache: использует расширение PHP APC. Этот вариант можно считать самым быстрым при работе с кешем для централизованного толстого приложения (например, для одного сервера, без специальных балансировщиков нагрузки и т.д.).
  • yii\caching\DbCache: использует таблицу базы данных для хранения кэшированных данных. Чтобы использовать этот кэш, вы должны создать таблицу, указанную в yii\caching\DbCache::$cacheTable.
  • yii\caching\DummyCache: служит в качестве заполнитель-заглушки кэша, который не выполняет никакого реального кэширования. Цель этого компонента - упростить код, который должен проверять доступность кеша. Например, во время разработки или если сервер не имеет фактической поддержки кэша, вы можете настроить компонент кэша для использования этого кэша. Когда включена фактическая поддержка кэширования, вы можете переключиться на использование соответствующего компонента кэша. В обоих случаях вы можете использовать тот же код Yii::$app->cache->get($key), чтобы попытаться получить данные из кэша, не опасаясь, что Yii::$app->cache может быть null.
  • yii\caching\FileCache: использует стандартные файлы для хранения кэшированных данных. Это особенно полезно для кэширования большой части данных, например, контента страницы.
  • yii\caching\MemCache: использует memcache PHP и memcached расширения. Этот вариант можно считать самым быстрым при работе с кешем в распределенных приложениях (например, с несколькими серверами, балансировщиками нагрузки и т. д.)
  • yii\redis\Cache: реализует компонент кэширования на основе хранилища ключей и значений Redis.
  • yii\caching\WinCache: использует расширение PHP WinCache.
  • yii\caching\XCache: использует расширение PHP XCache.
  • Zend Data Cache в качестве основного кэширующего носителя.

Кэширование API

Все компоненты кеша имеют один и тот же базовый класс yii\caching\Cache и, следовательно, поддерживают следующие API:

  • get(): извлекает элемент данных из кеша с помощью указанного ключа. Ложное значение будет возвращено, если элемент данных не найден в кэше или устарел/недействителен.
  • set(): сохраняет элемент данных, идентифицированный ключом в кеше.
  • add(): сохраняет элемент данных, идентифицированный ключом в кеше, если ключ не найден в кэше.
  • getOrSet(): извлекает элемент данных из кеша с указанным ключом или выполняет переданный обратный вызов, сохраняет возврат обратного вызова в кеш по ключу и возвращает эти данные.
  • multiGet(): извлекает несколько элементов данных из кеша с указанными ключами.
  • multiSet(): сохраняет несколько элементов данных в кеше. Каждый элемент идентифицируется ключом.
  • multiAdd(): хранит несколько элементов данных в кеше. Каждый элемент идентифицируется ключом. Если ключ уже существует в кэше, элемент данных будет пропущен.
  • exists(): возвращает значение, указывающее, найден ли указанный ключ в кэше.
  • delete(): удаляет элемент данных, идентифицированный ключом из кеша.
  • flush(): удаляет все элементы данных из кэша.

Некоторые хранилища кэшей, такие как MemCache, APC, поддерживают извлечение нескольких кэшированных значений в пакетном режиме, что может снизить накладные расходы, связанные с извлечением кэшированных данных. Для использования этой функции предусмотрены API multiGet() и multiAdd(). Если базовое хранилище кэшей не поддерживает эту функцию, оно будет имитироваться.

Поскольку yii\caching\Cache реализует ArrayAccess, компонент кэша может использоваться как массив. Вот несколько примеров:

$cache['var1'] = $value1;  // equivalent to: $cache->set('var1', $value1);
$value2 = $cache['var2'];  // equivalent to: $value2 = $cache->get('var2');

Ключи кеша

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

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

Общей стратегией определения ключа кэша является включение всех определяющих факторов в массив. Например, yii\db\Schema использует следующий ключ для кэширования информации схемы о таблице базы данных:

[
    __CLASS__,              // schema class name
    $this->db->dsn,         // DB connection data source name
    $this->db->username,    // DB connection login user
    $name,                  // table name
];

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

Когда одно и то же хранилище кэшей используется разными приложениями, вам следует указать уникальный префикс ключа кэша для каждого приложения, чтобы избежать конфликтов ключей кэша. Это можно сделать, настроив свойство yii\caching\Cache::$keyPrefix. Например, в конфигурации приложения вы можете написать следующий код:

'components' => [
    'cache' => [
        'class' => 'yii\caching\ApcCache',
        'keyPrefix' => 'myapp',       // a unique cache key prefix
    ],
],

Для обеспечения интероперабельности следует использовать только буквенно-цифровые символы.

Истечение срока действия кеша

Элемент данных, хранящийся в кеше, останется там навсегда, если он не будет удален из-за принудительного применения политики кэширования (например, заполнение кэшированного пространства и удаление самых старых данных). Чтобы изменить это поведение, вы можете указать параметр expiration при вызове set() для хранения элемента данных. Параметр указывает, на сколько секунд элемент данных может оставаться действительным в кэше. Когда вы вызываете get() для извлечения элемента данных, если прошло время истечения, метод вернет false, указывая, что элемент данных не найден в кэше. Например:

// keep the data in cache for at most 45 seconds
$cache->set($key, $data, 45);

sleep(50);

$data = $cache->get($key);
if ($data === false) {
    // $data is expired or is not found in the cache
}

Зависимости кэша

Помимо настройки срока действия, элемент кэшированных данных также может быть аннулирован изменениями так называемых зависимостей кэша. Например, yii\caching\FileDependency представляет зависимость времени модификации файла. Когда эта зависимость изменяется, это означает, что соответствующий файл изменен. В результате любое устаревшее содержимое файла, обнаруженное в кэше, должно быть аннулировано, а вызов get() должен возвращать false.

Зависимости кэшей представлены как объекты классов yii\caching\Dependency. Когда вы вызываете set() для хранения элемента данных в кэше, вы можете пройти вдоль связанного объекта зависимостей кэша. Например:

// Create a dependency on the modification time of file example.txt.
$dependency = new \yii\caching\FileDependency(['fileName' => 'example.txt']);

// The data will expire in 30 seconds.
// It may also be invalidated earlier if example.txt is modified.
$cache->set($key, $data, 30, $dependency);

// The cache will check if the data has expired.
// It will also check if the associated dependency was changed.
// It will return false if any of these conditions are met.
$data = $cache->get($key);

Ниже приведена сводка доступных зависимостей кеша:

  • yii\caching\ChainedDependency: зависимость изменяется, если какая-либо из зависимостей в цепочке изменена.
  • yii\caching\DbDependency: зависимость изменяется, если результат запроса указанного оператора SQL изменен.
  • yii\caching\ExpressionDependency: зависимость изменяется, если результат указанного выражения PHP изменен.
  • yii\caching\FileDependency: зависимость изменяется в случае изменения последнего времени изменения файла.
  • yii\caching\TagDependency: связывает кешированный элемент данных с одним или несколькими тегами. Вы можете аннулировать кэшированные элементы данных указанными тегами, вызывая yii\caching\TagDependency::invalidate().

Кэширование запросов

Кэширование запросов - это специальная функция кэширования, созданная поверх кеширования данных. Предусмотрено кэширование результатов запросов к базе данных.

Для кеширования запросов требуется соединение с БД и действительный компонент приложения кэша. Основное использование кэширования запросов заключается в следующем: при условии, что $db является экземпляром yii\db\Connection:

$result = $db->cache(function ($db) {

    // the result of the SQL query will be served from the cache
    // if query caching is enabled and the query result is found in the cache
    return $db->createCommand('SELECT * FROM customer WHERE id=1')->queryOne();

});

Кэширование запросов может использоваться как для DAO, так и для ActiveRecord:

$result = Customer::getDb()->cache(function ($db) {
    return Customer::find()->where(['id' => 1])->one();
});

Смыв кеша

Когда вам нужно сделать недействительными все сохраненные данные кэша, вы можете вызвать yii\caching\Cache::flush(). Вы можете очистить кэш от консоли, вызвав yii cache/flush.

  • yii cache: отображает доступные кеши в приложении
  • yii cache/flush cache1 cache2: очищает кэш-компоненты cache1, cache2 (вы можете передавать несколько имен компонентов, разделенных пробелом)
  • yii cache/flush-all: очищает все компоненты кеша приложения

Конфигурации

Кэширование запросов имеет три глобальных настраиваемых параметра через yii\db\Connection:

  • enableQueryCache: включить или выключить кэширование запросов. По умолчанию используется значение true. Обратите внимание, что для эффективного кэширования запросов вам также необходимо иметь действующий кеш, как указано в queryCache.
  • queryCacheDuration: это число секунд, в течение которых результат запроса может оставаться в кеше действительным. Вы можете использовать 0, чтобы указать, что результат запроса должен оставаться в кэше навсегда. Это свойство используется по умолчанию, когда yii\db\Connection::cache() вызывается без указания продолжительности.
  • queryCache: это идентификатор компонента приложения кэша. По умолчанию используется cache. Кэширование запросов разрешено только в том случае, если имеется действительный компонент приложения кэша.

Использование

Вы можете использовать yii\db\Connection::cache(), если у вас есть несколько SQL-запросов, которые должны использовать кэширование запросов. Использование заключается в следующем:

$duration = 60;     // cache query results for 60 seconds.
$dependency = ...;  // optional dependency

$result = $db->cache(function ($db) {

    // ... perform SQL queries here ...

    return $result;

}, $duration, $dependency);

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

Иногда в cache() вы можете отключить кэширование запросов для некоторых конкретных запросов. В этом случае вы можете использовать yii\db\Connection::noCache().

$result = $db->cache(function ($db) {

    // SQL queries that use query caching

    $db->noCache(function ($db) {

        // SQL queries that do not use query caching

    });

    // ...

    return $result;
});

Если вы просто хотите использовать кэширование запросов для одного запроса, вы можете вызвать yii\db\Command::cache() при создании команды. Например:

// use query caching and set query cache duration to be 60 seconds
$customer = $db->createCommand('SELECT * FROM customer WHERE id=1')->cache(60)->queryOne();

Вы также можете использовать yii\db\Command::noCache() для отключения кэширования запросов для одной команды. Например:

$result = $db->cache(function ($db) {

    // SQL queries that use query caching

    // do not use query caching for this command
    $customer = $db->createCommand('SELECT * FROM customer WHERE id=1')->noCache()->queryOne();

    // ...

    return $result;
});

Ограничения

Кэширование запросов не работает с результатами запросов, которые содержат обработчики ресурсов. Например, при использовании типа столбца BLOB в некоторых СУБД результат запроса будет возвращать обработчик ресурсов для данных столбца.

Некоторое кеширующее хранилище имеет ограничение по размеру. Например, memcache ограничивает максимальный размер каждой записи равным 1 МБ. Поэтому, если размер результата запроса превышает это ограничение, кэширование завершится неудачно.