Дата обновления перевода: 2020-12-25
Cache¶
Использование кэша - прекрасный способ ускорить ваше приложение. Компонент Symfony cache поставляется с множеством адаптеров для разных хранилищ. Каждый адаптер разработан для высокой производительности.
Следующий пример показывает типичное использование кэша:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | use Symfony\Contracts\Cache\ItemInterface;
// Функция будет запущена только при отсутствии значения в кэше
$value = $pool->get('my_cache_key', function (ItemInterface $item) {
$item->expiresAfter(3600);
// ... сделать HTTP запрос или сложные вычисления
$computedValue = 'foobar';
return $computedValue;
});
echo $value; // 'foobar'
// ... и удалить значение кэша по ключу
$pool->delete('my_cache_key');
|
Symfony поддерживает Cache Contracts, PSR-6/16 и интерфейсы Doctrine Cache. Вы можете прочитать больше о них в документации компонента.
Настройка кэша с FrameworkBundle¶
Когда настриваете компонент кэша есть несколько концепций о которых нужно знать:
- Pool
- Это сервис с которым вы будете взаимодействовать. Каждый пул будет всегда иметь своё пространство имён и закешированные элементы. Не бывает конфликтов между разными пулами.
- Adapter
- Адаптер - это шаблон, который вы используете для создания пула.
- Provider
- Провайдер - это сервис, который адаптеры используют для подключения к хранилищу. Примерами таких адаптеров являются Redis и Memcached. Если как провайдер используется DSN, то автоматически создаётся сервис.
Есть 2 пула, включённые по умолчанию. Это cache.app
и
cache.system
. Системный кэк используется для вещей вроде аннотаций, сериализатора
и валидации. cache.app
может использоваться в вашем коде. Вы можете настроить какой
адаптер (шаблон) они будут использовать используя app
и system
ключи как:
- YAML
1 2 3 4 5
# config/packages/cache.yaml framework: cache: app: cache.adapter.filesystem system: cache.adapter.system
- XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
<!-- config/packages/cache.xml --> <?xml version="1.0" encoding="UTF-8" ?> <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:framework="http://symfony.com/schema/dic/symfony" xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> <framework:config> <framework:cache app="cache.adapter.filesystem" system="cache.adapter.system" /> </framework:config> </container>
- PHP
1 2 3 4 5 6 7
// config/packages/cache.php $container->loadFromExtension('framework', [ 'cache' => [ 'app' => 'cache.adapter.filesystem', 'system' => 'cache.adapter.system', ], ]);
Компонент Cache поставляется с набором сконфигурированных адаптеров:
- cache.adapter.apcu
- cache.adapter.array
- cache.adapter.doctrine
- cache.adapter.filesystem
- cache.adapter.memcached
- cache.adapter.pdo
- cache.adapter.psr6
- cache.adapter.redis
- cache.adapter.redis_tag_aware (Redis adapter optimized to work with tags)
New in version 5.2: cache.adapter.redis_tag_aware
появился в Symfony 5.2.
Некоторые из этих адаптеров могут быть настроены с помощью сокращений. При использовании
этих сокращений создадутся пулы с id сервисов вида cache.[type]
.
- YAML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
# config/packages/cache.yaml framework: cache: directory: '%kernel.cache_dir%/pools' # Only used with cache.adapter.filesystem # service: cache.doctrine default_doctrine_provider: 'app.doctrine_cache' # service: cache.psr6 default_psr6_provider: 'app.my_psr6_service' # service: cache.redis default_redis_provider: 'redis://localhost' # service: cache.memcached default_memcached_provider: 'memcached://localhost' # service: cache.pdo default_pdo_provider: 'doctrine.dbal.default_connection'
- XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
<!-- config/packages/cache.xml --> <?xml version="1.0" encoding="UTF-8" ?> <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:framework="http://symfony.com/schema/dic/symfony" xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> <framework:config> <!-- default_doctrine_provider: Service: cache.doctrine default_psr6_provider: Service: cache.psr6 default_redis_provider: Service: cache.redis default_memcached_provider: Service: cache.memcached default_pdo_provider: Service: cache.pdo --> <!-- "directory" attribute is only used with cache.adapter.filesystem --> <framework:cache directory="%kernel.cache_dir%/pools" default_doctrine_provider="app.doctrine_cache" default_psr6_provider="app.my_psr6_service" default_redis_provider="redis://localhost" default_memcached_provider="memcached://localhost" default_pdo_provider="doctrine.dbal.default_connection" /> </framework:config> </container>
- PHP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// config/packages/cache.php $container->loadFromExtension('framework', [ 'cache' => [ // Only used with cache.adapter.filesystem 'directory' => '%kernel.cache_dir%/pools', // Service: cache.doctrine 'default_doctrine_provider' => 'app.doctrine_cache', // Service: cache.psr6 'default_psr6_provider' => 'app.my_psr6_service', // Service: cache.redis 'default_redis_provider' => 'redis://localhost', // Service: cache.memcached 'default_memcached_provider' => 'memcached://localhost', // Service: cache.pdo 'default_pdo_provider' => 'doctrine.dbal.default_connection', ], ]);
Создание пулов с другим пространством имён¶
Вы также можете создать пулы с другими настройками:
- YAML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
# config/packages/cache.yaml framework: cache: default_memcached_provider: 'memcached://localhost' pools: # создаст сервис "custom_thing.cache" # автоподключаемый через "CacheInterface $customThingCache" # использует конфигурацию кэша "app" custom_thing.cache: adapter: cache.app # создаёт сервис "my_cache_pool" # автоподключаемый через "CacheInterface $myCachePool" my_cache_pool: adapter: cache.adapter.filesystem # использует настройку выше default_memcached_provider acme.cache: adapter: cache.adapter.memcached # управление настройками адаптера foobar.cache: adapter: cache.adapter.memcached provider: 'memcached://user:[email protected]' # использует пул "foobar.cache" как бекенд, но настраивает # время жизни и как другие пулы выше имеет собственное пространство имён элементов кэша short_cache: adapter: foobar.cache default_lifetime: 60
- XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
<!-- config/packages/cache.xml --> <?xml version="1.0" encoding="UTF-8" ?> <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:framework="http://symfony.com/schema/dic/symfony" xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> <framework:config> <framework:cache default-memcached-provider="memcached://localhost"> <!-- creates a "custom_thing.cache" service autowireable via "CacheInterface $customThingCache" uses the "app" cache configuration --> <framework:pool name="custom_thing.cache" adapter="cache.app"/> <!-- creates a "my_cache_pool" service autowireable via "CacheInterface $myCachePool" --> <framework:pool name="my_cache_pool" adapter="cache.adapter.filesystem"/> <!-- uses the default_memcached_provider from above --> <framework:pool name="acme.cache" adapter="cache.adapter.memcached"/> <!-- control adapter's configuration --> <framework:pool name="foobar.cache" adapter="cache.adapter.memcached" provider="memcached://user:[email protected]" /> <!-- uses the "foobar.cache" pool as its backend but controls the lifetime and (like all pools) has a separate cache namespace --> <framework:pool name="short_cache" adapter="foobar.cache" default-lifetime="60"/> </framework:cache> </framework:config> </container>
- PHP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
// config/packages/cache.php $container->loadFromExtension('framework', [ 'cache' => [ 'default_memcached_provider' => 'memcached://localhost', 'pools' => [ // creates a "custom_thing.cache" service // autowireable via "CacheInterface $customThingCache" // uses the "app" cache configuration 'custom_thing.cache' => [ 'adapter' => 'cache.app', ], // creates a "my_cache_pool" service // autowireable via "CacheInterface $myCachePool" 'my_cache_pool' => [ 'adapter' => 'cache.adapter.filesystem', ], // uses the default_memcached_provider from above 'acme.cache' => [ 'adapter' => 'cache.adapter.memcached', ], // control adapter's configuration 'foobar.cache' => [ 'adapter' => 'cache.adapter.memcached', 'provider' => 'memcached://user:[email protected]', ], // uses the "foobar.cache" pool as its backend but controls // the lifetime and (like all pools) has a separate cache namespace 'short_cache' => [ 'adapter' => 'foobar.cache', 'default_lifetime' => 60, ], ], ], ]);
Каждый пул управляет набором независимых ключей кэша: ключи из разных пулов никогда не пересекаются, даже, если они используют один и тот же бекэнд. Это достигается добавлением префиксов к ключам с пространством имён, которое генерируется хешированием названия пула, имени скомпилированного класса контейнера и настраиваемым seed который по умолчанию равняется каталогу проекта.
Каждый кастомный пул становится сервисом, чей id является именем пула
(например, custom_thing.cache
). Алиас для автоподключения также создайтся для каждого пула
используя camel case версию его имени - например custom_thing.cache
может
автовнедряться при названии аргумента $customThingCache
с типом
CacheInterface
или
Psr\Cache\CacheItemPoolInterface
:
1 2 3 4 5 6 7 8 9 10 11 12 13 | use Symfony\Contracts\Cache\CacheInterface;
// в методе контроллера
public function listProducts(CacheInterface $customThingCache)
{
// ...
}
// в сервисе
public function __construct(CacheInterface $customThingCache)
{
// ...
}
|
Пользовательские настройки провайдеров¶
Некоторые провайдеры имеют специфические настройки конфигурации.
RedisAdapter позволяет вам
создавать провайдеров с настройками timeout
, retry_interval
и так далее. Для использования
этих настроек со значениями не по умолчанию нужно создать собственный провайдер \Redis
и использовать его при конфигурации пула.
- YAML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
# config/packages/cache.yaml framework: cache: pools: cache.my_redis: adapter: cache.adapter.redis provider: app.my_custom_redis_provider services: app.my_custom_redis_provider: class: \Redis factory: ['Symfony\Component\Cache\Adapter\RedisAdapter', 'createConnection'] arguments: - 'redis://localhost' - { retry_interval: 2, timeout: 10 }
- XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
<!-- config/packages/cache.xml --> <?xml version="1.0" encoding="UTF-8" ?> <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:framework="http://symfony.com/schema/dic/symfony" xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> <framework:config> <framework:cache> <framework:pool name="cache.my_redis" adapter="cache.adapter.redis" provider="app.my_custom_redis_provider"/> </framework:cache> </framework:config> <services> <service id="app.my_custom_redis_provider" class="\Redis"> <factory class="Symfony\Component\Cache\Adapter\RedisAdapter" method="createConnection"/> <argument>redis://localhost</argument> <argument type="collection"> <argument key="retry_interval">2</argument> <argument key="timeout">10</argument> </argument> </service> </services> </container>
- PHP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
// config/packages/cache.php use Symfony\Component\Cache\Adapter\RedisAdapter; $container->loadFromExtension('framework', [ 'cache' => [ 'pools' => [ 'cache.my_redis' => [ 'adapter' => 'cache.adapter.redis', 'provider' => 'app.my_custom_redis_provider', ], ], ], ]); $container->register('app.my_custom_redis_provider', \Redis::class) ->setFactory([RedisAdapter::class, 'createConnection']) ->addArgument('redis://localhost') ->addArgument([ 'retry_interval' => 2, 'timeout' => 10 ]) ;
Создание цепочки кэшей¶
Разные адаптеры кэшей имеют свои сильные и слабые стороны. Некоторые могут быть очень быстрыми, но оптимизированы для хранения небольших элементов, а некоторые могут хранить много данных, но достаточно медленные. Для получения лучшего от каждого вы можете использовать цепочку адаптеров.
Цепочка кэшей объединяет несколько пулов кэшей в один. При сохранении элемента в цепочку кэшей Symfony сохраняет его последовательно во все пулы. При получении элемента Symfony пытается получить его из первого пула. Если он не найден, пробует следующие пулы пока не найдёт элемент или не выбросится исключение. Из-за такого поведения рекомендуется определять адаптеры в цепочке начиная с самого быстрого и заканчивая самым медленным.
Если случается ошибка при сохранении элемента в пул, Symfony сохраняет его в других пулах и не выбрасывается исключение. Позже, при получении элемента, Symfony автоматически сохраняет элемент во всех недостающих пулах.
- YAML
1 2 3 4 5 6 7 8 9 10
# config/packages/cache.yaml framework: cache: pools: my_cache_pool: default_lifetime: 31536000 # 1 год adapters: - cache.adapter.array - cache.adapter.apcu - {name: cache.adapter.redis, provider: 'redis://user:[email protected]'}
- XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
<!-- config/packages/cache.xml --> <?xml version="1.0" encoding="UTF-8" ?> <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:framework="http://symfony.com/schema/dic/symfony" xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd"> <framework:config> <framework:cache> <framework:pool name="my_cache_pool" default-lifetime="31536000"> <framework:adapter name="cache.adapter.array"/> <framework:adapter name="cache.adapter.apcu"/> <framework:adapter name="cache.adapter.redis" provider="redis://user:[email protected]"/> </framework:pool> </framework:cache> </framework:config> </container>
- PHP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// config/packages/cache.php $container->loadFromExtension('framework', [ 'cache' => [ 'pools' => [ 'my_cache_pool' => [ 'default_lifetime' => 31536000, // One year 'adapters' => [ 'cache.adapter.array', 'cache.adapter.apcu', ['name' => 'cache.adapter.redis', 'provider' => 'redis://user:[email protected]'], ], ], ], ], ]);
Теги кэша¶
В приложениях с большим количеством элементов может быть полезно организовать сохранённые данные чтобы более эффективно инвалидировать кэш. Один из вариантов - использовать теги кэша. К элементу кэша можно добавить один или несколько тегов. Все элементы с тем же тегом могут быть инвалидированы с помощью вызова одной функции:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | use Symfony\Contracts\Cache\ItemInterface;
use Symfony\Contracts\Cache\TagAwareCacheInterface;
class SomeClass
{
private $myCachePool;
// используем автоподключение для внедрения пула кэша
public function __construct(TagAwareCacheInterface $myCachePool)
{
$this->myCachePool = $myCachePool;
}
public function someMethod()
{
$value0 = $this->myCachePool->get('item_0', function (ItemInterface $item) {
$item->tag(['foo', 'bar']);
return 'debug';
});
$value1 = $this->myCachePool->get('item_1', function (ItemInterface $item) {
$item->tag('foo');
return 'debug';
});
// Удалить все элементы ключа с тегом "bar"
$this->myCachePool->invalidateTags(['bar']);
}
}
|
Чтобы эта фича работала, нужно, чтобы адаптер кэша реализовал интерфейс
TagAwareCacheInterface
. Тогда можно использовать следующую настройку.
- YAML
1 2 3 4 5 6 7
# config/packages/cache.yaml framework: cache: pools: my_cache_pool: adapter: cache.adapter.redis tags: true
- XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
<!-- config/packages/cache.xml --> <?xml version="1.0" encoding="UTF-8" ?> <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:framework="http://symfony.com/schema/dic/symfony" xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> <framework:config> <framework:cache> <framework:pool name="my_cache_pool" adapter="cache.adapter.redis" tags="true"/> </framework:cache> </framework:config> </container>
- PHP
1 2 3 4 5 6 7 8 9 10 11
// config/packages/cache.php $container->loadFromExtension('framework', [ 'cache' => [ 'pools' => [ 'my_cache_pool' => [ 'adapter' => 'cache.adapter.redis', 'tags' => true, ], ], ], ]);
Теги сохраняются по умолчанию в том же пуле. Это подходит для большинства сценариев. Но иногда лучше хранить теги в другом пуле. Для этого укажите адаптер в настройках.
- YAML
1 2 3 4 5 6 7 8 9
# config/packages/cache.yaml framework: cache: pools: my_cache_pool: adapter: cache.adapter.redis tags: tag_pool tag_pool: adapter: cache.adapter.apcu
- XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
<!-- config/packages/cache.xml --> <?xml version="1.0" encoding="UTF-8" ?> <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:framework="http://symfony.com/schema/dic/symfony" xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd"> <framework:config> <framework:cache> <framework:pool name="my_cache_pool" adapter="cache.adapter.redis" tags="tag_pool"/> <framework:pool name="tag_pool" adapter="cache.adapter.apcu"/> </framework:cache> </framework:config> </container>
- PHP
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// config/packages/cache.php $container->loadFromExtension('framework', [ 'cache' => [ 'pools' => [ 'my_cache_pool' => [ 'adapter' => 'cache.adapter.redis', 'tags' => 'tag_pool', ], 'tag_pool' => [ 'adapter' => 'cache.adapter.apcu', ], ], ], ]);
Note
Интерфейс TagAwareCacheInterface
при автоподключении использует сервис cache.app
.
Очистка кэша¶
Для очистки кэша можно использовать команду bin/console cache:pool:clear [pool]
.
Это удалит все записи из вашего хранилища и нужно будет пересчитать
все значения. Вы также можете сгруппировать ваши пулы в "очистители кэша".
По умолчанию есть 3 очистителя кэша:
cache.global_clearer
cache.system_clearer
cache.app_clearer
Глобальный очиститель удалит все элементы кэша в каждом пуле. Системный очиститель кэша
используется при команде bin/console cache:clear
. App clearer - это очиститель по умолчанию.
Для просмотра всех доступных пулов кэша:
1 | $ php bin/console cache:pool:list
|
Очистить один пул:
1 | $ php bin/console cache:pool:clear my_cache_pool
|
Очистить все пользовательские пулы:
1 | $ php bin/console cache:pool:clear cache.app_clearer
|
Очистить все кэши везде:
1 | $ php bin/console cache:pool:clear cache.global_clearer
|
Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.