mirror of
				https://github.com/yiisoft/yii2.git
				synced 2025-11-01 03:26:36 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			346 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			346 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| Кэширование данных
 | ||
| ==================
 | ||
| 
 | ||
| Кэширование данных заключается в сохранении некоторой переменной PHP в кэше и её последующем извлечении. Оно является основой для расширенных возможностей, таких как [кэширование запросов](#query-caching)
 | ||
| и [кэширование страниц](caching-page.md).
 | ||
| 
 | ||
| Приведённый ниже код является типичным случаем кэширования данных, где `$cache` указывает на [компонент кэширования](#cache-components):
 | ||
| 
 | ||
| ```php
 | ||
| // Пробуем извлечь $data из кэша.
 | ||
| $data = $cache->get($key);
 | ||
| 
 | ||
| if ($data === false) {
 | ||
|     // $data нет в кэше, вычисляем заново
 | ||
|     $data = $this->calculateSomething();
 | ||
| 
 | ||
|     // Сохраняем значение $data в кэше. Данные можно получить в следующий раз.
 | ||
|     $cache->set($key, $data);
 | ||
| }
 | ||
| 
 | ||
| // Значение $data доступно здесь.
 | ||
| ```
 | ||
| 
 | ||
| Начиная с версии 2.0.11, [компонент кэширования](#cache-components) предоставляет метод
 | ||
| [[yii\caching\Cache::getOrSet()|getOrSet()]], который упрощает код при получении, вычислении и сохранении данных.
 | ||
| Приведённый ниже код делает в точности то же самое, что и код в предыдущем примере:
 | ||
| 
 | ||
| ```php
 | ||
| $data = $cache->getOrSet($key, function () {
 | ||
|     return $this->calculateSomething();
 | ||
| });
 | ||
| ```
 | ||
| 
 | ||
| Если в кэше есть данные по ключу `$key`, они будут сразу возвращены. 
 | ||
| Иначе, будет вызвана переданная анонимная функция, вычисляющая значение, которое будет сохранено в кэш и возвращено
 | ||
| из метода.
 | ||
| 
 | ||
| В случае, когда анонимной функции требуются данные из внешней области видимости, можно передать их с помощью
 | ||
| оператора `use`. Например:
 | ||
| 
 | ||
| ```php
 | ||
| $user_id = 42;
 | ||
| $data = $cache->getOrSet($key, function () use ($user_id) {
 | ||
|     return $this->calculateSomething($user_id);
 | ||
| });
 | ||
| ```
 | ||
| 
 | ||
| > Note: В [[yii\caching\Cache::getOrSet()|getOrSet()]] можно передать срок действия и зависимости кэша.
 | ||
|   Прочтите [Срок действия кэша](#cache-expiration) и [Зависимости кеша](#cache-dependencies) чтобы узнать больше.
 | ||
| 
 | ||
| 
 | ||
| ## Компоненты кэширования <span id="cache-components"></span>
 | ||
| 
 | ||
| Кэширование данных опирается на *компоненты кэширования*, которые представляют различные хранилища, такие как память, файлы и базы данных.
 | ||
| 
 | ||
| Кэш-компоненты, как правило, зарегистрированы в качестве [компонентов приложения](structure-application-components.md), так что их можно настраивать и обращаться к ним глобально. Следующий код показывает, как настроить компонент приложения `cache` для использования [Memcached](https://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',
 | ||
|     ],
 | ||
| ],
 | ||
| ```
 | ||
| 
 | ||
| > Tip: Вы можете зарегистрировать несколько кэш-компонентов приложения. Компонент с именем `cache` используется по умолчанию многими классами (например, [[yii\web\UrlManager]]).
 | ||
|    
 | ||
|    
 | ||
| ### Поддерживаемые хранилища <span id="supported-cache-storage"></span>
 | ||
| 
 | ||
| Yii поддерживает множество хранилищ кэша:
 | ||
| 
 | ||
| * [[yii\caching\ApcCache]]: использует расширение PHP [APC](https://www.php.net/manual/ru/book.apcu.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](https://www.php.net/manual/ru/book.memcache.php) и [memcached](https://www.php.net/manual/ru/book.memcached.php). Этот вариант может рассматриваться как самый быстрый при работе в распределенных приложениях (например, с несколькими серверами, балансировкой нагрузки и так далее);
 | ||
| * [[yii\redis\Cache]]: реализует компонент кэша на основе [Redis](https://redis.io/), хранилища ключ-значение (требуется Redis версии 2.6.12 или выше);
 | ||
| * [[yii\caching\WinCache]]: использует расширение PHP [WinCache](https://iis.net/downloads/microsoft/wincache-extension) ([смотрите также](https://www.php.net/manual/ru/book.wincache.php));
 | ||
| * [[yii\caching\XCache]] _(deprecated)_: использует расширение PHP [XCache](https://en.wikipedia.org/wiki/List_of_PHP_accelerators#XCache);
 | ||
| * [[yii\caching\ZendDataCache]] _(deprecated)_: использует [Zend Data Cache](https://files.zend.com/help/Zend-Server-6/zend-server.htm#data_cache_component.htm).
 | ||
| 
 | ||
| > Tip: Вы можете использовать разные способы хранения кэша в одном приложении. Общая стратегия заключается в использовании памяти под хранение небольших часто используемых данных (например, статистические данные). Для больших и реже используемых данных (например, содержимое страницы) лучше использовать файлы или базу данных.
 | ||
|   
 | ||
| ## Кэш API, <span id="cache-apis"></span>
 | ||
| 
 | ||
| У всех компонентов кэша имеется один базовый класс [[yii\caching\Cache]] со следующими методами:
 | ||
| 
 | ||
| * [[yii\caching\Cache::get()|get()]]: возвращает данные по указанному ключу. Если данные не найдены или устарели, то
 | ||
|   значение `false` будет возвращено;
 | ||
| * [[yii\caching\Cache::set()|set()]]: сохраняет данные по ключу;
 | ||
| * [[yii\caching\Cache::add()|add()]]: сохраняет данные по ключу если такого ключа ещё нет;
 | ||
| * [[yii\caching\Cache::getOrSet()|getOrSet()]]: возвращает данные по указанному ключу или выполняет переданную
 | ||
|   анонимную функцию для вычисления значения, а полученные данные сохраняет в кэш и возвращает;  
 | ||
| * [[yii\caching\Cache::multiGet()|multiGet()]]: извлекает сразу несколько элементов данных из кэша по заданным ключам;
 | ||
| * [[yii\caching\Cache::multiSet()|multiSet()]]: сохраняет несколько элементов данных. Каждый элемент идентифицируется ключом;
 | ||
| * [[yii\caching\Cache::multiAdd()|multiAdd()]]: сохраняет несколько элементов данных. Каждый элемент идентифицируется ключом.
 | ||
|   Если ключ уже существует, то сохранения не происходит;
 | ||
| * [[yii\caching\Cache::exists()|exists()]]: есть ли указанный ключ в кэше;
 | ||
| * [[yii\caching\Cache::delete()|delete()]]: удаляет указанный ключ;
 | ||
| * [[yii\caching\Cache::flush()|flush()]]: удаляет все данные.
 | ||
| 
 | ||
| > Note: Не кэшируйте непосредственно значение `false`, потому что [[yii\caching\Cache::get()|get()]] использует
 | ||
| `false` для случая, когда данные не найдены в кэше. Вы можете обернуть `false` в массив и закэшировать его, чтобы
 | ||
| избежать данной проблемы.
 | ||
| 
 | ||
| Некоторые кэш-хранилища, например, MemCache или APC, поддерживают получение нескольких значений в пакетном режиме, что
 | ||
| может сократить накладные расходы на получение данных. Данную возможность возможно использовать при помощи
 | ||
| [[yii\caching\Cache::multiGet()|multiGet()]] и [[yii\caching\Cache::multiAdd()|multiAdd()]]. В случае, если хранилище
 | ||
| не поддерживает эту функцию, она будет имитироваться.
 | ||
| 
 | ||
| Так как [[yii\caching\Cache]] реализует `ArrayAccess` - следовательно компонент кэша можно использовать как массив:
 | ||
| 
 | ||
| ```php
 | ||
| $cache['var1'] = $value1;  // эквивалентно: $cache->set('var1', $value1);
 | ||
| $value2 = $cache['var2'];  // эквивалентно: $value2 = $cache->get('var2');
 | ||
| ```
 | ||
| 
 | ||
| 
 | ||
| ### Ключи кэша <span id="cache-keys"></span>
 | ||
| 
 | ||
| Каждый элемент данных, хранящийся в кэше, идентифицируется ключом. Когда вы сохраняете элемент данных в кэше, необходимо указать для него ключ. Позже, когда вы извлекаете элемент данных из кэша, вы также должны предоставить соответствующий ключ.
 | ||
| 
 | ||
| Вы можете использовать строку или произвольное значение в качестве ключа кэша. Если ключ не строка, то он будет автоматически сериализован в строку.
 | ||
| 
 | ||
| Обычно ключ задаётся массивом всех значимых частей. Например, для хранения информации о таблице в [[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', // уникальный префикс ключей кэша
 | ||
|     ],
 | ||
| ],
 | ||
| ```
 | ||
| 
 | ||
| Для обеспечения совместимости должны быть использованы только алфавитно-цифровые символы.
 | ||
| 
 | ||
| ### Срок действия кэша <span id="cache-expiration"></span>
 | ||
| 
 | ||
| Элементы данных, хранимые в кэше, остаются там навсегда и могут быть удалены только из-за особенностей функционирования хранилища (например, место для кэширования заполнено и старые данные удаляются). Чтобы изменить этот режим, вы можете передать истечение срока действия ключа при вызове метода [[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) {
 | ||
|     // срок действия истек или ключ не найден в кэше
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| Начиная с версии 2.0.11 вы можете изменить значение по умолчанию (бесконечность) для длительности кэширования задав
 | ||
| [[yii\caching\Cache::$defaultDuration|defaultDuration]] в конфигурации компонента кэша. Таким образом, можно будет
 | ||
| не передавать значение `duration` в [[yii\caching\Cache::set()|set()]] каждый раз.
 | ||
| 
 | ||
| ### Зависимости кэша <span id="cache-dependencies"></span>
 | ||
| 
 | ||
| В добавок к изменению срока действия ключа элемент может быть признан недействительным из-за *изменения зависимостей*. К примеру, [[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\CallbackDependency]]: зависимость меняется, если результат коллбэк функции изменён;
 | ||
| - [[yii\caching\FileDependency]]: зависимость меняется, если изменилось время последней модификации файла;
 | ||
| - [[yii\caching\TagDependency]]: Связывает кэшированные данные элемента с одним или несколькими тегами. Вы можете аннулировать кэширование данных элементов с заданным тегом(тегами) по вызову. [[yii\caching\TagDependency::invalidate()]];
 | ||
| 
 | ||
| ## Кэширование запросов <span id="query-caching"></span>
 | ||
| 
 | ||
| Кэширование запросов - это специальная функция, построенная на основе кэширования данных.
 | ||
| Она предназначена для кэширования результатов запросов к базе данных.
 | ||
| 
 | ||
| Кэширование запросов требует [[yii\db\Connection|DB connection]] действительный `cache` компонента приложения. Предпологая, что `$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):
 | ||
| 
 | ||
| ```php
 | ||
| $result = Customer::getDb()->cache(function ($db) {
 | ||
|     return Customer::find()->where(['id' => 1])->one();
 | ||
| });
 | ||
| ```
 | ||
| 
 | ||
| > Info: Некоторые СУБД (например, [MySQL](https://dev.mysql.com/doc/refman/5.6/en/query-cache.html)) поддерживают кэширование запросов любого механизма на стороне сервера БД. КЗ описано разделом выше. Оно имеет безусловное преимущество, поскольку, благодаря ему, можно указать гибкие зависимости кэша и это более эффективно.
 | ||
| 
 | ||
| 
 | ||
| ### Очистка кэша <span id="cache-flushing"></span>
 | ||
| 
 | ||
| Для очистки всего кэша, вы можете вызвать [[yii\caching\Cache::flush()]].
 | ||
| 
 | ||
| Также вы можете очистить кэш из консоли, вызвав `yii cache/flush`.
 | ||
|  - `yii cache`: отображает список доступных кэширующих компонентов приложения
 | ||
|  - `yii cache/flush cache1 cache2`: очищает кэш в компонентах `cache1`, `cache2` (можно передать несколько названий
 | ||
|  компонентов кэширования, разделяя их пробелом)
 | ||
|  - `yii cache/flush-all`: очищает кэш во всех кэширующих компонентах приложения
 | ||
|  - `yii cache/flush-schema db`: очищает кеш схемы базы данных для данного компонента соединения
 | ||
| 
 | ||
| > Info: Консольное приложение использует отдельный конфигурационный файл по умолчанию. Для получения должного
 | ||
| результата, убедитесь, что в конфигурациях консольного и веб-приложения у вас одинаковые компоненты кэширования.
 | ||
| 
 | ||
| 
 | ||
| ### Конфигурации <span id="query-caching-configs"></span>
 | ||
| 
 | ||
| Кэширование запросов имеет три глобальных конфигурационных параметра через [[yii\db\Connection]]:
 | ||
| 
 | ||
| * [[yii\db\Connection::enableQueryCache|enableQueryCache]]: включить или выключить кэширование запросов; По умолчанию `true`. Стоит отметить, что для использования кэширования вам может понадобиться компонент cache, как показано в [[yii\db\Connection::queryCache|queryCache]];
 | ||
| * [[yii\db\Connection::queryCacheDuration|queryCacheDuration]]: количество секунд кэширования результата. Для бесконечного кэша используйте `0`. Именно это значение выставляется [[yii\db\Connection::cache()]], если не указать время явно;
 | ||
| * [[yii\db\Connection::queryCache|queryCache]]: ID компонента кэширования. По умолчанию `'cache'`. Кэширования запросов работает только в том случае, если используется компонент приложения кэш.
 | ||
| 
 | ||
| ### Использование <span id="query-caching-usages"></span>
 | ||
| 
 | ||
| Вы можете использовать [[yii\db\Connection::cache()]], если у вас есть несколько SQL запросов, которые необходимо закэшировать:
 | ||
| 
 | ||
| ```php
 | ||
| $duration = 60;     // кэширование результата на 60 секунд
 | ||
| $dependency = ...;  // параметры зависимости
 | ||
| 
 | ||
| $result = $db->cache(function ($db) {
 | ||
| 
 | ||
|     // ... выполнять SQL запросы здесь ...
 | ||
| 
 | ||
|     return $result;
 | ||
| 
 | ||
| }, $duration, $dependency);
 | ||
| ```
 | ||
| 
 | ||
| Любые 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;
 | ||
| });
 | ||
| ```
 | ||
| 
 | ||
| 
 | ||
| ### Ограничения <span id="query-caching-limitations"></span>
 | ||
| 
 | ||
| Кэширование запросов не работает с результатами запросов, которые содержат обработчики ресурсов. Например, при использовании столбца типа `BLOB` в некоторых СУБД, в качестве результата запроса будет возвращен указатель на ресурс для этого столбца данных.
 | ||
| 
 | ||
| Некоторые кэш хранилища имеют ограничение в размере данных. Например, Memcache ограничивает максимальный размер каждой записи до 1 Мб. Таким образом, если результат запроса превышает этот предел, данные не будут закэшированы.
 | 
