<? phpukraine ДОКУМЕНТАЦІЯ
Пошук по платформі
Документація українською

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

ВЕРСІЯ
АКТУАЛЬНА Актуальний реліз. Переклад наздоганяє оригінал — розділи з низьким відсотком позначені у змісті.
ПОГЛИБЛЕНО · ЧАСТКОВО частковий переклад, оновлено 5 вересня 2026

Кеш — Symfony

ПЕРЕКЛАД НЕПОВНИЙ

Частину підрозділів ще не перекладено — вони показані англійською нижче в тексті або лишились в оригіналі. Готові фрагменти вже перевірені редактором.

Кешування покращує продуктивність застосунку, зберігаючи результат дорогих операцій — запитів до бази даних, HTTP-запитів до зовнішніх сервісів чи важких обчислень, — щоб наступні запити могли прочитати його миттєво, замість обчислювати наново.

Компонент Symfony Cache реалізує і стандарт PSR-6, і простіші Cache Contracts для максимальної сумісності. Він створений заради продуктивності та стійкості й постачається з готовими до використання адаптерами для найпоширеніших бекендів кешування: Redis, Memcached, APCu, реляційні бази даних, файлова система тощо. Він також надає розширені можливості, як-от інвалідація на основі тегів і запобігання «навалі» на кеш (stampede prevention). Ви можете використовувати його в будь-якому PHP-застосунку, із Symfony або без неї.

Дивіться також

Ця стаття пояснює, як кешувати довільні дані в коді вашого застосунку. Якщо ви хочете кешувати цілі HTTP-відповіді й віддавати їх без запуску застосунку, прочитайте статтю HTTP Cache.

Встановлення

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

$ composer require symfony/cache

Якщо ви встановлюєте цей компонент поза застосунком Symfony, вам потрібно підключити файл vendor/autoload.php у вашому коді, щоб увімкнути механізм автозавантаження класів, наданий Composer. Прочитайте цю статтю для докладнішої інформації.

Пули кешу, адаптери та елементи

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

Елемент (Item) : Одна одиниця інформації, збережена як пара ключ/значення, де ключ — це унікальний ідентифікатор інформації, а значення — її вміст.

Пул (Pool) : Логічний репозиторій елементів кешу. Усі операції кешу (збереження елементів, пошук елементів тощо) виконуються через пул. Застосунки можуть визначати скільки завгодно пулів, і ключі кешу з різних пулів ніколи не конфліктують, навіть якщо вони мають спільний бекенд.

Адаптер (Adapter) : Клас, що реалізує власне механізм кешування для зберігання інформації у файловій системі, на сервері Redis, у базі даних тощо. Коли ви використовуєте компонент у будь-якому PHP-застосунку, ви створюєте пули шляхом інстанціювання адаптерів. У Symfony-застосунках адаптери — це шаблони, які використовуються для створення пулів через конфігурацію (наприклад, cache.adapter.redis).

Провайдер (Provider) : Сервіс, який деякі адаптери використовують для підключення до сховища (наприклад, зʼєднання з Redis або Memcached). Це поняття існує лише в Symfony-застосунках; коли як провайдер використовується рядок DSN, сервіс зʼєднання створюється автоматично.

Cache Contracts проти PSR-6

Цей компонент містить два різні підходи до кешування:

Cache Contracts: : Простий, але потужний спосіб кешувати значення на основі колбеків перерахунку.

Кешування PSR-6: : Універсальна система кешування, яка оперує пулами кешу та елементами кешу.

Рекомендується використовувати підхід Cache Contracts: він потребує менше шаблонного коду й типово надає захист від навали на кеш. Саме тому ця стаття пояснює всі можливості кешування через Cache Contracts. Якщо натомість вам потрібен універсальний API PSR-6 (наприклад, при інтеграції сторонньої бібліотеки, яка його вимагає), прочитайте розділ про використання пулів кешу PSR-6 у кінці цієї статті, який пояснює, як виконувати ті самі операції за допомогою того API.

Базове використання

Cache Contracts визначають лише два методи: get() і delete(). Методу set() немає, бо метод get() і отримує, і встановлює значення кешу.

Перше, що вам потрібно, — це пул кешу, тобто обʼєкт, що реалізує Symfony\Contracts\Cache\CacheInterface. У Symfony-застосунках вкажіть цей інтерфейс як тип аргументу сервісу чи контролера, щоб отримати впровадження пулу cache.app. Коли ви використовуєте компонент у будь-якому PHP-застосунку, створіть пул шляхом інстанціювання одного з адаптерів кешу:

// src/Service/NewsProvider.php
namespace App\Service;

use Symfony\Contracts\Cache\CacheInterface;

class NewsProvider
{
    // пул "cache.app" впроваджується завдяки autowiring
    public function __construct(
        private CacheInterface $cache,
    ) {
    }

    // ...
}
use Symfony\Component\Cache\Adapter\FilesystemAdapter;

$cache = new FilesystemAdapter();

Тепер ви можете отримувати й видаляти кешовані дані за допомогою цього обʼєкта. Перший аргумент методу get() — це ключ, довільний рядок, який ви асоціюєте з кешованим значенням, щоб пізніше його отримати. Другий аргумент — це PHP callable, який виконується, коли ключ не знайдено в кеші, щоб згенерувати й повернути значення:

use Symfony\Contracts\Cache\ItemInterface;

// callable буде виконано лише при промаху кешу
$value = $cache->get('my_cache_key', function (ItemInterface $item): string {
    $item->expiresAfter(3600);

    // ... виконайте якийсь HTTP-запит або важкі обчислення
    $computedValue = 'foobar';

    return $computedValue;
});

echo $value; // 'foobar'

// ... а щоб видалити ключ кешу
$cache->delete('my_cache_key');

Зверніть увагу

Використовуйте теги кешу, щоб видалити більше одного ключа за раз.

Колбек, переданий у метод get(), надає дві додаткові можливості:

  • Виявлення раннього закінчення терміну дії: викличте метод Symfony\Contracts\Cache\ItemInterface::isHit усередині колбека; якщо він повертає true, значення перераховується наперед, до настання дати закінчення терміну дії, через механізм запобігання навалі на кеш;
  • Відкидання значення: колбек може приймати другий аргумент bool &$save, переданий за посиланням. Якщо ви встановите $save у false всередині колбека, повернуте значення не буде збережено в бекенді.

Елементи кешу

Елементи кешу — це одиниці інформації, збережені в кеші як пара ключ/значення. У компоненті Cache вони представлені класом Symfony\Component\Cache\CacheItem. Вони використовуються і в Cache Contracts, і в інтерфейсах PSR-6.

Ключі та значення елементів кешу

Ключ елемента кешу — це звичайний рядок, який виступає його ідентифікатором, тож він має бути унікальним для кожного пулу кешу. Ви можете вільно обирати ключі, але вони мають містити лише літери (A-Z, a-z), цифри (0-9) та символи _ і .. Інші поширені символи (як-от { } ( ) / \ @ :) зарезервовані стандартом PSR-6 для майбутнього використання.

Значення елемента кешу може бути будь-якими даними, представленими типом, який PHP здатний серіалізувати, як-от базові типи (string, integer, float, boolean, null), масиви та обʼєкти.

Створення елементів кешу

Єдиний спосіб створити елементи кешу — через пули кешу. При використанні Cache Contracts вони передаються як аргументи в колбек перерахунку, де ви можете їх налаштувати. Наприклад, ви можете встановити їхній час закінчення терміну дії:

$productsCount = $cache->get('stats.products_count', function (ItemInterface $item): int {
    $item->expiresAfter(3600); // кешувати на 1 годину

    // ... обчислити значення
    return 4711;
});

Значення елемента кешу (тобто значення, повернуте колбеком) встановлюється автоматично, тож вам не доведеться мати справу з елементом кешу поза колбеком.

Термін дії елемента кешу

Типово елементи кешу зберігаються назавжди. На практиці це «постійне зберігання» може дуже різнитися залежно від адаптера, який ви використовуєте (наприклад, адаптер APCu втрачає свій вміст при перезапуску сервера).

Проте в деяких застосунках прийнято використовувати елементи кешу з коротшим часом життя. Розгляньмо, наприклад, застосунок, який кешує останні новини лише на одну хвилину. У таких випадках використовуйте метод expiresAfter(), щоб задати кількість секунд кешування елемента:

$latestNews->expiresAfter(60);  // 60 секунд = 1 хвилина

// цей метод також приймає екземпляри \DateInterval
$latestNews->expiresAfter(DateInterval::createFromDateString('1 hour'));

Елементи кешу визначають ще один повʼязаний метод — expiresAt(), — щоб задати точну дату й час, коли термін дії елемента закінчиться:

$mostPopularNews->expiresAt(new \DateTime('tomorrow'));

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

Доступні адаптери кешу

Адаптери кешу — це класи, що реалізують власне механізм кешування. Усі вони підтримують Cache Contracts та інтерфейси PSR-6, тож ви можете створювати пули кешу з будь-яким із них.

У Symfony-застосунках кілька адаптерів попередньо налаштовані як сервіси, тож ви можете використовувати їх при визначенні своїх пулів кешу:

Зверніть увагу

Є також спеціальний адаптер cache.adapter.system. Його рекомендується використовувати для системного кешу. Цей адаптер застосовує певну логіку, щоб динамічно обрати найкраще можливе сховище на основі вашої системи (або PHP-файли, або APCu).

Компонент надає й інші адаптери, які не є попередньо налаштованими сервісами в Symfony-застосунках (наприклад, ChainAdapter і PhpFilesAdapter). Прочитайте окрему статтю про кожен адаптер, щоб дізнатися, як їх налаштувати й використовувати:

  • cache/adapters/*

Налаштування кешу за допомогою FrameworkBundle

У Symfony-застосунках пули кешу визначаються конфігурацією framework.cache. Деякі з вбудованих адаптерів можна налаштувати через скорочення, які визначають типовий провайдер, використовуваний усіма пулами на основі цього адаптера:

# config/packages/cache.yaml
framework:
    cache:
        directory: '%kernel.cache_dir%/pools' # Only used with cache.adapter.filesystem

        default_doctrine_dbal_provider: 'doctrine.dbal.default_connection'
        default_psr6_provider: 'app.my_psr6_service'
        default_redis_provider: 'redis://localhost'
        default_valkey_provider: 'valkey://localhost'
        default_memcached_provider: 'memcached://localhost'
        default_pdo_provider: 'pgsql:host=localhost'
// config/packages/cache.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;

return App::config([
    'framework' => [
        'cache' => [
            'directory' => '%kernel.cache_dir%/pools', // Використовується лише з cache.adapter.filesystem
            'default_doctrine_dbal_provider' => 'doctrine.dbal.default_connection',
            'default_psr6_provider' => 'app.my_psr6_service',
            'default_redis_provider' => 'redis://localhost',
            'default_valkey_provider' => 'valkey://localhost',
            'default_memcached_provider' => 'memcached://localhost',
            'default_pdo_provider' => 'pgsql:host=localhost',
        ],
    ],
]);

Системний кеш і кеш застосунку

Два пули кешу завжди увімкнені типово: cache.system і cache.app.

cache.system використовується внутрішньо компонентами Symfony, як-от анотації, серіалізатор і валідація. Він також доступний для коду застосунку, але лише за певних обмежень:

  1. Записи мають виводитися з вихідного коду й бути відтворюваними під час прогріву кешу через CacheWarmer.
  2. Кешований вміст має змінюватися лише тоді, коли змінюється вихідний код (тобто при розгортанні, а не під час виконання); ставтеся до нього як до readonly після розгортання.

Типово cache.system використовує cache.adapter.system, який пише у файлову систему й ланцюжком підключає APCu, коли той доступний. У більшості випадків типовий варіант — правильний вибір.

Порада

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

cache.app — це універсальний кеш даних для коду застосунку й бандлів. Дані в цьому пулі не потрібно скидати при розгортанні. Типово він використовує cache.adapter.filesystem, але рекомендується налаштувати швидший адаптер, як-от Redis, коли він доступний (це гарантує, що кешовані дані переживуть розгортання й будуть спільними для кількох інстансів у багатосерверній конфігурації).

Власні пули типово використовують cache.app як свій адаптер, якщо не налаштовано інакше. При використанні autowiring cache.app автоматично впроваджується в будь-який аргумент сервісу, типізований як Psr\Cache\CacheItemPoolInterface, Symfony\Contracts\Cache\CacheInterface або Symfony\Contracts\Cache\NamespacedPoolInterface.

Ви можете налаштувати адаптер, який використовує кожен наперед визначений пул, через ключі app і system:

# config/packages/cache.yaml
framework:
    cache:
        app: cache.adapter.filesystem
        system: cache.adapter.system
// config/packages/cache.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;

return App::config([
    'framework' => [
        'cache' => [
            'app' => 'cache.adapter.filesystem',
            'system' => 'cache.adapter.system',
        ],
    ],
]);

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

Ви також можете створювати більш налаштовані пули:

# config/packages/cache.yaml
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
// config/packages/cache.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;

return App::config([
    'framework' => [
        'cache' => [
            'default_memcached_provider' => 'memcached://localhost',
            'pools' => [
                // створює сервіс "custom_thing.cache"
                // autowireable через "CacheInterface $customThingCache"
                // використовує конфігурацію кешу "app"
                'custom_thing.cache' => [
                    'adapter' => 'cache.app',
                ],
                // створює сервіс "my_cache_pool"
                // autowireable через "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,
                ],
            ],
        ],
    ],
]);

Кожен пул керує набором незалежних ключів кешу: ключі з різних пулів ніколи не конфліктують, навіть якщо вони мають спільний бекенд. Це досягається додаванням до ключів префікса — простору імен, згенерованого хешуванням назви пулу, назви класу адаптера кешу та налаштовуваного «зерна» (seed), яке типово дорівнює каталогу проєкту й класу скомпільованого контейнера.

Кожен власний пул стає сервісом, ID якого — це назва пулу (наприклад, custom_thing.cache). Для кожного пулу також створюється autowiring-аліас, що використовує camel case версію його назви: наприклад, custom_thing.cache можна впровадити автоматично, назвавши аргумент $customThingCache і типізувавши його або Symfony\Contracts\Cache\CacheInterface, або Psr\Cache\CacheItemPoolInterface:

use Symfony\Contracts\Cache\CacheInterface;
// ...

// з методу контролера
public function listProducts(CacheInterface $customThingCache): Response
{
    // ...
}

// у сервісі
public function __construct(private CacheInterface $customThingCache)
{
    // ...
}

Коли ви використовуєте компонент у будь-якому PHP-застосунку, простір імен пулів задається першим аргументом конструктора адаптерів, який також дозволяє налаштувати типовий час життя елементів:

use Symfony\Component\Cache\Adapter\FilesystemAdapter;

// створює пул, який додає до своїх ключів простір імен "my_app" і
// елементи якого типово закінчують термін дії через годину
$cache = new FilesystemAdapter('my_app', 3600);

Порада

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

# 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 і використати його при налаштуванні пулу.

# 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 }
// config/packages/cache.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Symfony\Component\Cache\Adapter\RedisAdapter;

return App::config([
    'framework' => [
        'cache' => [
            'pools' => [
                'cache.my_redis' => [
                    'adapter' => 'cache.adapter.redis',
                    'provider' => 'app.my_custom_redis_provider',
                ],
            ],
        ],
    ],
    'services' => [
        'app.my_custom_redis_provider' => [
            'class' => \Redis::class,
            'factory' => [RedisAdapter::class, 'createConnection'],
            'arguments' => ['redis://localhost', ['retry_interval' => 2, 'timeout' => 10]],
        ],
    ],
]);

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

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

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

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

# 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:[email protected]'}
// config/packages/cache.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;

return App::config([
    'framework' => [
        'cache' => [
            'pools' => [
                'my_cache_pool' => [
                    'default_lifetime' => 31536000, // Один рік
                    'adapters' => [
                        'cache.adapter.array',
                        'cache.adapter.apcu',
                        ['name' => 'cache.adapter.redis', 'provider' => 'redis://user:[email protected]'],
                    ],
                ],
            ],
        ],
    ],
]);
use Symfony\Component\Cache\Adapter\ApcuAdapter;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\Cache\Adapter\ChainAdapter;
use Symfony\Component\Cache\Adapter\RedisAdapter;

$cache = new ChainAdapter([
    new ArrayAdapter(),
    new ApcuAdapter(),
    new RedisAdapter(
        RedisAdapter::createConnection('redis://user:[email protected]')
    ),
], 31536000); // один рік

Створення підпросторів імен

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

У таких випадках Symfony дозволяє створювати різні контексти кешу за допомогою просторів імен. Простір імен кешу — це довільний рядок, який ідентифікує набір повʼязаних елементів кешу. Більшість адаптерів кешу, наданих компонентом, реалізують Symfony\Contracts\Cache\NamespacedPoolInterface, який надає метод Symfony\Contracts\Cache\NamespacedPoolInterface::withSubNamespace (деякі адаптери, як-от PhpArrayAdapter і Psr16Adapter, не підтримують простори імен).

Цей метод дозволяє поміщати кешовані елементи в простір імен, прозоро додаючи префікс до їхніх ключів:

$userCache = $cache->withSubNamespace(sprintf('user-%d', $user->getId()));

$userCache->get('dashboard_data', function (ItemInterface $item): string {
    $item->expiresAfter(3600);

    return '...';
});

У цьому прикладі елемент кешу використовує ключ dashboard_data, але внутрішньо він буде збережений у просторі імен на основі ID поточного користувача. Це обробляється автоматично, тож вам не потрібно вручну додавати префікси до ключів, як-от user-27.dashboard_data.

У Symfony-застосунках пул cache.app і всі пули, визначені під опцією framework.cache.pools, підтримують підпростори імен. Ви також можете впровадити пул cache.app, типізувавши аргумент сервісу чи контролера як Symfony\Contracts\Cache\NamespacedPoolInterface.

Немає жодних настанов чи обмежень щодо того, як визначати простори імен кешу. Ви можете зробити їх настільки деталізованими або узагальненими, наскільки цього вимагає ваш застосунок:

$localeCache = $cache->withSubNamespace($request->getLocale());

$flagCache = $cache->withSubNamespace(
    $featureToggle->isEnabled('new_checkout') ? 'checkout-v2' : 'checkout-v1'
);

$channel = $request->attributes->get('_route')?->startsWith('api_') ? 'api' : 'web';
$channelCache = $cache->withSubNamespace($channel);

Порада

Ви можете поєднувати простори імен кешу з тегами кешу для більш складних потреб.

Немає вбудованого способу інвалідувати кеші за простором імен. Натомість рекомендований підхід — змінити сам простір імен. Саме тому прийнято включати статичні або динамічні дані версіонування до простору імен кешу:

// для простих застосунків може вистачити статичного номера версії, що інкрементується
$userCache = $cache->withSubNamespace(sprintf('v1-user-%d', $user->getId()));

// інші застосунки можуть використовувати динамічне версіонування на основі дати (наприклад, щомісячне)
$userCache = $cache->withSubNamespace(sprintf('%s-user-%d', date('Ym'), $user->getId()));

// або навіть інвалідувати кеш, коли дані користувача змінюються
$checksum = hash('xxh128', $user->getUpdatedAt()->format(DATE_ATOM));
$userCache = $cache->withSubNamespace(sprintf('user-%d-%s', $user->getId(), $checksum));

Запобігання навалі на кеш

Cache Contracts мають вбудоване запобігання навалі (Stampede prevention). Це усуває сплески навантаження на CPU в моменти, коли кеш холодний. Якщо приклад застосунку витрачає 5 секунд на обчислення даних, які кешуються на 1 годину, і ці дані запитуються 10 разів щосекунди, це означає, що переважно ви маєте влучання в кеш і все гаразд. Але через 1 годину застосунок отримує 10 нових запитів до холодного кешу. Тож дані обчислюються знову. Наступної секунди стається те саме. Тож дані обчислюються близько 50 разів, доки кеш не прогріється знову. Ось де вам потрібне запобігання навалі.

Перше рішення — використання блокування: дозволяти лише одному PHP-процесу (у межах хоста) обчислювати конкретний ключ за раз. Блокування вбудоване типово, тож вам не потрібно робити нічого, крім використання Cache Contracts.

Друге рішення також вбудоване при використанні Cache Contracts: замість чекати на повну затримку до закінчення терміну дії значення, перерахувати його наперед, до дати закінчення терміну дії. Алгоритм ймовірнісного раннього закінчення терміну дії (probabilistic early expiration) випадковим чином імітує промах кешу для одного користувача, тоді як інші й далі отримують кешоване значення. Ви можете керувати його поведінкою третім необовʼязковим параметром методу Symfony\Contracts\Cache\CacheInterface::get, який є числом із рухомою комою під назвою «beta».

Типово beta дорівнює 1.0, і вищі значення означають раніший перерахунок. Встановіть його в 0, щоб вимкнути ранній перерахунок, і в INF, щоб примусити негайний перерахунок:

use Symfony\Contracts\Cache\ItemInterface;

$beta = 1.0;
$value = $cache->get('my_cache_key', function (ItemInterface $item): string {
    $item->expiresAfter(3600);

    return '...';
}, $beta);

Зверніть увагу

Параметр beta має ефект лише тоді, коли кешований елемент визначає закінчення терміну дії. Крім того, запобігання навалі застосовується лише до Cache Contracts: при використанні методів PSR-6 (getItem(), save() тощо) ви маєте самі захищати застосунок від навали на кеш.

Налаштування власного marshaller для окремого пулу кешу

Нововведення у версії 8.1

Опцію marshaller для пулів кешу було введено в Symfony 8.1.

Наведена вище конфігурація декорує сервіс cache.default_marshaller, тож власний marshaller застосовується до всіх пулів кешу. Якщо вам потрібен власний marshaller лише для конкретних пулів, використовуйте натомість опцію marshaller:

# config/packages/cache.yaml
framework:
    cache:
        pools:
            cache.encrypted:
                adapter: cache.adapter.filesystem
                marshaller: 'app.sodium_marshaller'

services:
    app.sodium_marshaller:
        class: Symfony\Component\Cache\Marshaller\SodiumMarshaller
        arguments:
            - ['%env(base64:CACHE_DECRYPTION_KEY)%']
            - '@cache.default_marshaller'
// config/packages/cache.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Symfony\Component\Cache\Marshaller\SodiumMarshaller;

return App::config([
    'framework' => [
        'cache' => [
            'pools' => [
                'cache.encrypted' => [
                    'adapter' => 'cache.adapter.filesystem',
                    'marshaller' => 'app.sodium_marshaller',
                ],
            ],
        ],
    ],
    'services' => [
        'app.sodium_marshaller' => [
            'class' => SodiumMarshaller::class,
            'arguments' => [
                [env('CACHE_DECRYPTION_KEY')->base64()],
                service('cache.default_marshaller'),
            ],
        ],
    ],
]);

Цей підхід дозволяє поєднувати різні стратегії marshalling між пулами (наприклад, шифрувати дані в одному пулі й стискати їх в іншому), залишаючи іншим пулам типовий marshaller:

# config/packages/cache.yaml
framework:
    cache:
        pools:
            cache.tokens:
                adapter: cache.adapter.redis
                marshaller: 'app.sodium_marshaller'
            cache.large_data:
                adapter: cache.adapter.filesystem
                marshaller: 'app.deflate_marshaller'
            cache.regular:
                adapter: cache.adapter.filesystem

services:
    app.sodium_marshaller:
        class: Symfony\Component\Cache\Marshaller\SodiumMarshaller
        arguments:
            - ['%env(base64:CACHE_DECRYPTION_KEY)%']
            - '@cache.default_marshaller'

    app.deflate_marshaller:
        class: Symfony\Component\Cache\Marshaller\DeflateMarshaller
        arguments:
            - '@cache.default_marshaller'
// config/packages/cache.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Symfony\Component\Cache\Marshaller\DeflateMarshaller;
use Symfony\Component\Cache\Marshaller\SodiumMarshaller;

return App::config([
    'framework' => [
        'cache' => [
            'pools' => [
                'cache.tokens' => [
                    'adapter' => 'cache.adapter.redis',
                    'marshaller' => 'app.sodium_marshaller',
                ],
                'cache.large_data' => [
                    'adapter' => 'cache.adapter.filesystem',
                    'marshaller' => 'app.deflate_marshaller',
                ],
                'cache.regular' => [
                    'adapter' => 'cache.adapter.filesystem',
                ],
            ],
        ],
    ],
    'services' => [
        'app.sodium_marshaller' => [
            'class' => SodiumMarshaller::class,
            'arguments' => [
                [env('CACHE_DECRYPTION_KEY')->base64()],
                service('cache.default_marshaller'),
            ],
        ],
        'app.deflate_marshaller' => [
            'class' => DeflateMarshaller::class,
            'arguments' => [
                service('cache.default_marshaller'),
            ],
        ],
    ],
]);

Асинхронне обчислення значень кешу

Через алгоритм ймовірнісного раннього закінчення терміну дії деякі елементи кешу обираються для раннього закінчення, доки вони ще свіжі. Типово такі елементи кешу перераховуються синхронно. Проте в Symfony-застосунках ви можете обчислювати їх асинхронно, делегувавши обчислення значення фоновому обробнику за допомогою компонента Messenger. У цьому випадку, коли елемент запитується, його кешоване значення повертається негайно, а через шину Messenger надсилається Symfony\Component\Cache\Messenger\EarlyExpirationMessage.

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

Спершу створіть сервіс, який обчислюватиме значення елемента:

// src/Cache/CacheComputation.php
namespace App\Cache;

use Psr\Cache\CacheItemInterface;
use Symfony\Contracts\Cache\CallbackInterface;

class CacheComputation implements CallbackInterface
{
    public function __invoke(CacheItemInterface $item, bool &$save): string
    {
        $item->expiresAfter(5);

        // це випадковий приклад; тут ви маєте виконати власне обчислення
        return sprintf('#%06X', mt_rand(0, 0xFFFFFF));
    }
}

Це значення кешу буде запитано з контролера, іншого сервісу тощо. У наступному прикладі значення запитується з контролера:

// src/Controller/CacheController.php
namespace App\Controller;

use App\Cache\CacheComputation;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Cache\ItemInterface;

class CacheController extends AbstractController
{
    #[Route('/cache', name: 'cache')]
    public function index(CacheInterface $asyncCache, CacheComputation $cacheComputation): Response
    {
        // передайте в кеш метод сервісу, який оновлює елемент
        $cachedValue = $asyncCache->get('my_value', $cacheComputation);

        // ...
    }
}

Нарешті, налаштуйте новий пул кешу (наприклад, названий async.cache), який використовуватиме шину повідомлень для обчислення значень в обробнику:

# config/packages/framework.yaml
framework:
    cache:
        pools:
            async.cache:
                early_expiration_message_bus: messenger.default_bus

    messenger:
        transports:
            async_bus: '%env(MESSENGER_TRANSPORT_DSN)%'
        routing:
            'Symfony\Component\Cache\Messenger\EarlyExpirationMessage': async_bus
// config/packages/framework.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Symfony\Component\Cache\Messenger\EarlyExpirationMessage;

return App::config([
    'framework' => [
        'cache' => [
            'pools' => [
                'async.cache' => [
                    'early_expiration_message_bus' => 'messenger.default_bus',
                ],
            ],
        ],
        'messenger' => [
            'transports' => [
                'async_bus' => env('MESSENGER_TRANSPORT_DSN'),
            ],
            'routing' => [
                EarlyExpirationMessage::class => 'async_bus',
            ],
        ],
    ],
]);

Тепер ви можете запустити споживача:

$ php bin/console messenger:consume async_bus

Ось і все! Тепер, щоразу коли елемент запитується з цього пулу кешу, його кешоване значення повертатиметься негайно. Якщо він буде обраний для раннього закінчення терміну дії, через шину буде надіслано повідомлення, щоб запланувати фонове обчислення для оновлення значення.

Інвалідація кешу

Інвалідація кешу — це процес видалення всіх кешованих елементів, повʼязаних зі зміною стану вашої моделі. Найпростіший вид інвалідації — це пряме видалення елемента. Але коли стан первинного ресурсу поширився на кілька кешованих елементів, підтримувати їх синхронізованими може бути складно.

Компонент Symfony Cache надає два механізми, що допомагають розвʼязати цю проблему:

  • Інвалідація на основі тегів — для керування залежностями даних;
  • Інвалідація на основі закінчення терміну дії — для залежностей, повʼязаних із часом.

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

У застосунках із багатьма ключами кешу може бути корисно організувати збережені дані так, щоб інвалідувати кеш ефективніше. Для цього прикріпіть один або кілька тегів до кожного кешованого елемента. Кожен тег — це звичайний рядковий ідентифікатор, який ви можете будь-коли використати, щоб видалити всі елементи, повʼязані з цим тегом.

Щоб прикріпити теги до кешованих елементів, використовуйте метод Symfony\Contracts\Cache\ItemInterface::tag. Щоб видалити всі елементи, повʼязані з певним тегом, використовуйте метод Symfony\Contracts\Cache\TagAwareCacheInterface::invalidateTags пулу кешу:

use Symfony\Contracts\Cache\ItemInterface;
use Symfony\Contracts\Cache\TagAwareCacheInterface;

class SomeClass
{
    // використання autowiring для впровадження пулу "my_cache_pool",
    // визначеного в конфігурації, показаній нижче
    public function __construct(
        private TagAwareCacheInterface $myCachePool,
    ) {
    }

    public function someMethod(): void
    {
        $value0 = $this->myCachePool->get('item_0', function (ItemInterface $item): string {
            $item->tag(['foo', 'bar']);

            return 'debug';
        });

        $value1 = $this->myCachePool->get('item_1', function (ItemInterface $item): string {
            $item->tag('foo');

            return 'debug';
        });

        // видалити всі ключі кешу, позначені тегом "bar"
        $this->myCachePool->invalidateTags(['bar']);

        // якщо ви знаєте ключ кешу, ви також можете видалити елемент напряму
        $this->myCachePool->delete('item_1');
    }
}
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Symfony\Component\Cache\Adapter\TagAwareAdapter;
use Symfony\Contracts\Cache\ItemInterface;

$cache = new TagAwareAdapter(new FilesystemAdapter());

$value0 = $cache->get('item_0', function (ItemInterface $item): string {
    $item->tag(['foo', 'bar']);

    return 'debug';
});

$value1 = $cache->get('item_1', function (ItemInterface $item): string {
    $item->tag('foo');

    return 'debug';
});

// видалити всі ключі кешу, позначені тегом "bar"
$cache->invalidateTags(['bar']);

// якщо ви знаєте ключ кешу, ви також можете видалити елемент напряму
$cache->delete('item_1');

Інвалідація за тегами дуже зручна, коли відстежувати ключі кешу стає складно.

Щоб увімкнути цю можливість, пул кешу має реалізовувати Symfony\Contracts\Cache\TagAwareCacheInterface. У Symfony-застосунках використовуйте опцію tags пулу:

# config/packages/cache.yaml
framework:
    cache:
        pools:
            my_cache_pool:
                adapter: cache.adapter.redis_tag_aware
                tags: true
// config/packages/cache.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;

return App::config([
    'framework' => [
        'cache' => [
            'pools' => [
                'my_cache_pool' => [
                    'adapter' => 'cache.adapter.redis_tag_aware',
                    'tags' => true,
                ],
            ],
        ],
    ],
]);

Зверніть увагу

У Symfony-застосунках, коли аргумент сервісу чи контролера типізовано як Symfony\Contracts\Cache\TagAwareCacheInterface, autowiring впроваджує сервіс cache.app.taggable — пул із підтримкою тегів на основі пулу cache.app. Це означає, що ви можете використовувати теги кешу, не визначаючи жодного власного пулу.

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

# config/packages/cache.yaml
framework:
    cache:
        pools:
            my_cache_pool:
                adapter: cache.adapter.redis
                tags: tag_pool
            tag_pool:
                adapter: cache.adapter.apcu
// config/packages/cache.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;

The following subsections of the original article are not yet translated: the remainder of "Using Cache Tags" (the PHP configuration example continued and tag-aware adapter notes), "Pruning Cache Items", "Encrypting the Cache", "Clearing the Cache", "Using Cache Tags with the Redis Adapter" (redis-tag-aware-adapter), "Using PSR-6 Cache Pools" (cache-component-psr6-caching), including "Looking for Cache Items", "Saving Cache Items", "Removing Cache Items", and the sections on "Cache Pool Pruning", "Marshalling", "Adapter Reference" and "Learn more".

ЯК ЦЯ СТОРІНКА ВИГЛЯДАЄ В ПОШУКУ
phpukraine.com/docs/symfony/cache
Кеш — Symfony документація українською
Кеш у Symfony 8.1: переклад офіційної документації українською. Оновлено 5 вересня 2026. Приклади коду, пояснення та посилання на питання зі співбесід.
Стан перекладу

Перекладаємо з офіційної документації, розділ за розділом, і не ховаємо недоперекладене. Помітили неточність у терміні чи реченні: напишіть, виправимо.

79%
Готовності
5
У роботі
0
Ще не перекладено
Глосарій термінів