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

Кэширование данных — это сохранение некоторой переменной PHP в кэше и последующее её извлечение оттуда.
Это также является основой для более расширенных возможностей кэширования, например, [кэширование запросов](#query-caching)
и [кэширование страниц](caching-page.md).

Следующий код является типичным случаем использования модели кэширования данных, где `$cache` относится к [компоненту кеширования](#cache-components):

```php
// Извлечение $data из кэша
$data = $cache->get($key);

if ($data === false) {

    // $data не найдена в кэше, получить по новой

    // сохранить $data в кэше так, чтобы данные можно было получить в следующий раз
    $cache->set($key, $data);
}

// $data данные доступны тут
```


## Компоненты кеширования <a name="cache-components"></a>

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

Кэш-компоненты, как правило, зарегистрированы в качестве [компонентов приложения](structure-application-components.md), так
что они могут быть глобально настраиваемыми и доступными. 
Следующий код показывает, как настроить `cache` компонент приложения, для использования [Memcached](http://memcached.org/) с двумя кэш-серверами:

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

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

Поскольку все компоненты кэша поддерживают тот же набор API-интерфейсов, вы можете поменять основной компонент кэша на другой компонент кэша путём перенастройки конфигурации приложения не меняя код, использующий кэш.
Например, вы можете изменить конфигурацию, чтобы использовать [[yii\caching\ApcCache|APC cache]]:


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

> Совет: Вы можете зарегистрировать несколько кэш-компонентов приложения. Компонент с именем `cache` используется 
   по умолчанию со многими классами (например, [[yii\web\UrlManager]]).
   
   
### Поддерживаются следующие кэш-хранилища <a name="supported-cache-storage"></a>

Yii поддерживает широкий круг кэш-хранилищь. Краткая сводка:

* [[yii\caching\ApcCache]]: использует PHP [APC](http://php.net/manual/en/book.apc.php) расширение. Эта опция возможно считается
    самой быстрой при работе с кэшем для централизованного высоконагруженного приложения (т.е. один сервер, без выделенного балансировщика нагрузки и т.д.).
* [[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]]: использует PHP [memcache](http://php.net/manual/en/book.memcache.php)
  and [memcached](http://php.net/manual/en/book.memcached.php) расширение. Эта опция может рассматриваться как самая быстрая при работе с кэшем в распределенных приложениях (например, с несколькими серверами, балансировки нагрузки и т.д.)
* [[yii\redis\Cache]]: реализует компонент кэша на основе [Redis](http://redis.io/), хранилище ключ-значение (Redis версия 2.6.12 или выше).
* [[yii\caching\WinCache]]: использует PHP [WinCache](http://iis.net/downloads/microsoft/wincache-extension)
  ([see also](http://php.net/manual/en/book.wincache.php)) расширение.
* [[yii\caching\XCache]]: использует PHP [XCache](http://xcache.lighttpd.net/) расширение.
* [[yii\caching\ZendDataCache]]: использует
  [Zend Data Cache](http://files.zend.com/help/Zend-Server-6/zend-server.htm#data_cache_component.htm)
  в качестве базовой среды кэширования.


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

## Кэш API, <a name="cache-apis"> </a>

У всех компонентов кэша один базовый класс [[yii\caching\Cache]] таким образом, поддерживаются следующие интерфейсы:

* [[yii\caching\Cache::get()|get()]]: возвращает данные из кэша с указанным ключом. Значение false
  будет возвращено, если данные не найдены в кэше или их срок истек/недействителен.
* [[yii\caching\Cache::set()|set()]]: сохраняет данные идентифицированные по ключу кэша.
* [[yii\caching\Cache::add()|add()]]: сохраняет элемент данных, идентифицированный ключом в кэше, если ключ не найден в кэше.
* [[yii\caching\Cache::mget()|mget()]]: извлекает несколько элементов данных из кэша с заданными ключами.
* [[yii\caching\Cache::mset()|mset()]]: хранит множество элементов данных в кэше. Каждый элемент идентифицируется ключом.
* [[yii\caching\Cache::madd()|madd()]]: хранит множество элементов данных в кэше. Каждый элемент идентифицируется ключом.
  Если ключ уже существует в кэше, элемент данных будет пропущен.
* [[yii\caching\Cache::exists()|exists()]]: возвращает значение, указывающее, является ли указанный ключ найденным в кэше.
* [[yii\caching\Cache::delete()|delete()]]: удаляет элемент данных, определенных с помощью ключа из кэша.
* [[yii\caching\Cache::flush()|flush()]]: удаляет все элементы данных из кэша.

> Примечание: Не кэшируйте непосредственно значение `false`, потому что [[yii\caching\Cache::get()|get()]] использует метод
`false` как возвращаемое значение, чтобы указать, на то, что данные не найдены в кэше. Вы можете обернуть false` в `массив` и закэшировать этот массив, чтобы избежать данной проблемы.

Некоторые кэш-хранилища, например, MemCache или APC, поддерживают загрузку нескольких кэшированных значений в пакетном режиме,
что может уменьшить накладные расходы на получение кэшированных данных. API-интерфейсы [[yii\caching\Cache::mget()|mget()]]
и [[yii\caching\Cache::madd()|madd()]] используют эту особенность. В случае, если кэш-хранилище
не поддерживает эту функцию, то она будет имитироваться.

Потому что [[yii\caching\Cache]] принадлежащий Аррей Акссесу как компонент может использоваться как массив. Ниже приведены некоторые примеры:

```php
$cache['var1'] = $value1;  // эквивалентно: $cache->set('var1', $value1);
$value2 = $cache['var2'];  // эквивалентно: $value2 = $cache->get('var2');
```


### Ключи кэша <a name="cache-keys"></a>

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

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

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

```php
[
    __CLASS__,              // название класса схемы
    $this->db->dsn,         // Данные подключения, базы данных
    $this->db->username,    // Данные подключения, логин пользователя
    $name,                  // название таблицы
];
```

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

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

```php
'components' => [
    'cache' => [
        'class' => 'yii\caching\ApcCache',
        'keyPrefix' => 'myapp', // уникальная приставка ключей кеша
    ],
],
```

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


### Срок действия кэша <a name="cache-expiration"></a>

Элементы данных, которые хранятся в кэше остаются там навсегда, если только они не будут удалены из-за некоторых условий функционирования кэша (например, место для кэширования заполнено и старые данные удаляются). Чтобы изменить этот  режим, вы можете предоставить истечение срока действия параметра при вызове метода [[yii\caching\Cache::set()|set()]], для хранения элемента данных. Параметр указывает, на сколько секунд элемент данных может оставаться в кэше. Когда вы вызываете [[yii\caching\Cache::get()|get()]], чтобы получить элемент данных, если его срок годности прошел, то метод вернется ложным (false), что указывает на то, что элемент данных не найден в кэше. Например:


```php
// Хранить данные в кэше не более 45 секунд
$cache->set($key, $data, 45);

sleep(50);

$data = $cache->get($key);
if ($data === false) {
    // $data срок действия истек или не найден в кэше
}
```


### Зависимости кэша <a name="cache-dependencies"></a>

Кроме настроек срока действия кэша, кешированный элемент может быть признан недействительным из-за изменения зависимостей к примеру [[yii\caching\FileDependency]] представляет собой зависимость времени изменения файла когда эта зависимость(имеется ввиду время) изменяется эт значит что файл изменился. Как результат, любой устаревший контент найденный в кеше должен быть признан недействительным и вызов [[yii\caching\Cache::get()|get()]] вернет false.

Зависимости кэша представлены в виде объектов [[yii\caching\Dependency]] классов-потомков. Когда вы вызываете метод [[yii\caching\Cache::set()|set()]], чтобы сохранить элемент данных в кэше, вы можете пройти по соответствующей кэш-зависимости объекта. Например:

```php
// Создать зависимость от времени модификации файла example.txt.
$dependency = new \yii\caching\FileDependency(['fileName' => 'example.txt']);

// Данные истекают через 30 секунд.
// Данные могут стать недействительными и раньше, если example.txt будет изменен.
$cache->set($key, $data, 30, $dependency);

// Кэш будет проверен, если данные истекли.
// Он также будет проверен, если указанная зависимость была изменена.
// Вернется false, если какое-либо из этих условий выполнено.
$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()]].


## Кэширование запросов <a name="query-caching"></a>

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

Кеширование запросов требует [[yii\db\Connection|DB connection]] и действительный `Кэш компонент` приложения. Простое использование
запросов кэширования происходит следующим образом, предпологая что `$db` это экземпляр [[yii\db\Connection]]:

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

    // Результат SQL запроса будет возвращен из кэша
    // Если кэширование запросов включено и результат запроса присутствует в кэше
    return $db->createCommand('SELECT * FROM customer WHERE id=1')->queryOne();

});
```

Кэширование запросов может быть использованно для [DAO](db-dao.md), а также [ActiveRecord](db-active-record.md).

> Информация: Некоторые СУБД (например, [MySQL](http://dev.mysql.com/doc/refman/5.1/en/query-cache.html)) также поддерживает кэширование запросов на стороне сервера БД. Вы можете использовать любой механизм кэширования запросов. Кэширование запросов описанное выше, имеет преимущество, поскольку можно указать гибкие зависимости кэша и это более эффективно.


### Конфигурации <a name="query-caching-configs"></a>

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

* [[yii\db\Connection::enableQueryCache|enableQueryCache]]: whether to turn on or off query caching.
  It defaults to true. Note that to effectively turn on query caching, you also need to have a valid
  cache, as specified by [[yii\db\Connection::queryCache|queryCache]].
* [[yii\db\Connection::queryCacheDuration|queryCacheDuration]]: this represents the number of seconds
  that a query result can remain valid in the cache. You can use 0 to indicate a query result should
  remain in the cache forever. This property is the default value used when [[yii\db\Connection::cache()]]
  is called without specifying a duration.
* [[yii\db\Connection::queryCache|queryCache]]: this represents the ID of the cache application component.
  It defaults to `'cache'`. Query caching is enabled only if there is a valid cache application component.


### Использование <a name="query-caching-usages"></a>

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

```php
$duration = 60;     // кэширование результата на 60 секунд
$dependency = ...;  // параметры зависимости

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

    // ... выполнять SQL запросы здесь ...

    return $result;

}, $duration, $dependency);
```

Любые SQL запросы в анонимной функции будут кэшироваться в течении указанного промежутка времени с заданной зависимостью.
Если результат запроса в кэше - дейтсвительный, запрос будет пропущен и результат будет возвращен из кеша, вметсо SQL запроса.
Если вы не укажите '$duration' параметра, значение [[yii\db\Connection::queryCacheDuration|queryCacheDuration]] будет использоваться вместо этого.

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

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

    // SQL запросы, которые используют кэширование

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

        // SQL запросы, которые не используют кэширование

    });

    // ...

    return $result;
});
```

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

```php
// использовать кэширование запросов и установить срок действия кэша на 60 секунд
$customer = $db->createCommand('SELECT * FROM customer WHERE id=1')->cache(60)->queryOne();
```

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

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

    // Используется кэширование SQL запросов

    // не использовать кэширование запросов для этой команды
    $customer = $db->createCommand('SELECT * FROM customer WHERE id=1')->noCache()->queryOne();

    // ...

    return $result;
});
```


### Ограничения <a name="query-caching-limitations"></a>

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

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