Кеш

Дата оновлення перекладу 2022-12-11

Кеш

Використання кешу - чудовий спосіб прискорити ваш додаток. Компонент 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

Коли ви налаштовуєте компонент Cache, є декілька концепцій, про які варто знати:

Пул
Це сервіс з яким ви будете взаємодіяти. Кожний пул завжди буде мати свій простір імен і кешовані елементи. Н буває конфліктів між різними пулами.
Адаптер
Адаптер - це шаблон, який ви використовуєте для створення пулу.
Провайдер
Провайдер - це сервіс, який адаптери використовують для підключення до сховища. Прикладами таких адаптерів є Redis та Memcached. Якщо в якості провайдеру використовується DSN, то автоматично створється сервіс.

Є 2 пули, включені за замовчуванням. Це cache.app та cache.system. Системний кеш використовується для речей на кшталт анотацій, серіалізатора та валідації. cache.app може використовуватися у вашому коді. Ви можете налаштувати, який адаптер (шаблон) вони будуть використовувати, використавши ключі app та system як:

  • YAML
  • XML
  • PHP
1
2
3
4
5
# config/packages/cache.yaml
framework:
    cache:
        app: cache.adapter.filesystem
        system: cache.adapter.system

Tip

Хоча переконфігурувати кеш system можливо, рекомендовано залишити кофігурацію за замовчуванням, застосовану Symfony.

Компонент Cache постачається з набором сконфігурованих адаптерів:

Деякі з цих адаптерів можуть бути налаштовані з допомогою скорочень. При використанні цих скорочень, будуть створені пули з 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' # Використовується лише з cache.adapter.filesystem

        # сервіс: cache.doctrine
        default_doctrine_provider: 'app.doctrine_cache'
        # сервіс: cache.psr6
        default_psr6_provider: 'app.my_psr6_service'
        # сервіс: cache.redis
        default_redis_provider: 'redis://localhost'
        # сервіс: cache.memcached
        default_memcached_provider: 'memcached://localhost'
        # сервіс: cache.pdo
        default_pdo_provider: 'doctrine.dbal.default_connection'
  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
# config/packages/cache.yaml
framework:
    cache:
        directory: '%kernel.cache_dir%/pools' # Only used with cache.adapter.filesystem

        # сервіс: cache.psr6
        default_psr6_provider: 'app.my_psr6_service'
        # сервіс: cache.redis
        default_redis_provider: 'redis://localhost'
        # сервіс: cache.memcached
        default_memcached_provider: 'memcached://localhost'
        # сервіс: cache.pdo
        default_pdo_provider: 'doctrine.dbal.default_connection'

Створення користувацьких пулів (з простором імен)

Ви також можете створити пули з іншими налаштуваннями:

  • YAML
  • XML
  • 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
# 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:password@example.com'

            # використовує пул "foobar.cache" як бекенд, але налаштовує час життя
            # і, як інші пули вище, має власний простір імен елементів кешу
            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)
{
    // ...
}

Tip

Якщо вам потрібно, щоб простір імен був інтероперабельним зі стороннім додатком, то ви можете контролювати автоматичне генерування, встановивши атрибут namespace сервісного тегу cache.pool. Наприклад, ви можете перевизначити сервісне визначення адаптера:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
# config/services.yaml
services:
    # ...

    app.cache.adapter.redis:
        parent: 'cache.adapter.redis'
        tags:
            - { name: 'cache.pool', namespace: 'my_custom_namespace' }

Користувацькі налаштування провайдерів

Деякі провайдери мають специфічні налаштування конфігурації. RedisAdapter дозволяє вам створювати провайдерів з налаштуваннями timeout, retry_interval і так далі. Для використання цих налаштувань зі значеннями не за замовчуванням, потрібео створити власний провайдер \Redis та використовувати його при конфігурації пулу.

  • YAML
  • XML
  • PHP
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 }

Створення ланцюжку кешів

Різні адаптери кешів мають свої сильні та слабкі сторони. Деякі можуть бути дуже швидкими, але оптимізованими для зберігання невеликих елементів, а деякі можуть зберігати багато даних, але достатньо повільні. Для отримання кращого від кожного, ви можете використовувати ланцюжок адаптерів.

Ланцюжок кешів обʼєднує декілька пулів кешів в один. При збереженні елемента у ланцюжок кешів, Symfony зберігає його послідовно в усі пули. При отриманні елемента Symfony намагається отримати його з першого пулу. Якщо його не знайдено, пробує наступні пули, поки не знайде елемент або не зʼявиться виключення. Через таку поведінку, рекомендується визначати адаптери у ланцюжку починаючи з найшвидшого і закінчуючи найповільнішим.

Якщо трапляється помилка при збереженні елмента в пул, Symfony зберігає його в інших пулах і не викликає виключення. Пізніше, при отриманні елемента, Symfony
автоматично зберігає елемент у всіх пулах, яких не вистачає.

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
# config/packages/cache.yaml
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:password@example.com'}

Використання тегів кешу

У додатках з великоою кількістю елементів може бути корисно організовувати збережені дані, щоб ефективніше інвалідувати кеш. Один з варіантів - використовувати теги кешу. До елемента кешу можна додати один або декілька тегів. Всі елементи з однаковим тегом можуть бути інвалідовані за допомогою виклику однієї функції:

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
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
  • XML
  • PHP
1
2
3
4
5
6
7
# config/packages/cache.yaml
framework:
    cache:
        pools:
            my_cache_pool:
                adapter: cache.adapter.redis
                tags: true

Теги зберігаються в одному пулі за замовчуванням. Це добре у більшості випадків. Але інколи може бути краще зберігати теги в іншому пулі. Цього можна досягти за допомогою вказання адаптера.

  • YAML
  • XML
  • PHP
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

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

Очистити кеш за тегом(ами):

6.1

Команда cache:pool:invalidate-tags була представлена в Symfony 6.1.

1
2
3
4
5
6
7
8
9
10
11
# інвалідувати tag1 в усіх тегованих пулів
$ php bin/console cache:pool:invalidate-tags tag1

# інвалідувати tag1 і tag2 в усіх тегованих пулів
$ php bin/console cache:pool:invalidate-tags tag1 tag2

# інвалідувати tag1 і tag2 в пулі cache.app
$ php bin/console cache:pool:invalidate-tags tag1 tag2 --pool=cache.app

# інвалідувати tag1 і tag2 в пулах cache1 і cache2 
$ php bin/console cache:pool:invalidate-tags tag1 tag2 -p cache1 -p cache2

Шифрування кешу

Для того, щоб зашифрувати кеш, використовуючи libsodium, ви можете використати SodiumMarshaller.

Для почтаку, вам необхідно згенерувати безпечний ключ та додати його в своє сховище секретів у вигляді CACHE_DECRYPTION_KEY:

1
$ php -r 'echo base64_encode(sodium_crypto_box_keypair());'

Потім, зареєструйте сервіс SodiumMarshaller, використовуючи цей ключ:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
# config/packages/cache.yaml

# ...
services:
    Symfony\Component\Cache\Marshaller\SodiumMarshaller:
        decorates: cache.default_marshaller
        arguments:
            - ['%env(base64:CACHE_DECRYPTION_KEY)%']
            # використовуйте декілька ключів, щоб міняти їх
            #- ['%env(base64:CACHE_DECRYPTION_KEY)%', '%env(base64:OLD_CACHE_DECRYPTION_KEY)%']
            - '@Symfony\Component\Cache\Marshaller\SodiumMarshaller.inner'

Caution

Це зашифрує значення обʼєктів кешу, але не ключі кешу. Будьте обережні, щоб не припускати витіку конфіденційних даних у ключах кешу.

При конфігурації декількох ключів, перший ключ буде використовуватися для читання та запису, а додатковий(і) ключ(і) будуть використовуватися лише для читання. Коли всі обʼєкти кешу будуть зашифровані, а у старого ключа скінчиться строк дії, ви можете повністю видалити OLD_CACHE_DECRYPTION_KEY.