Кеш — це інструмент, який прискорює систему, але якщо робити його “наосліп”, він починає брехати. У CMS це особливо боляче: редактор змінив заголовок, а на сайті ще 10 хвилин старий; змінився статус публікації, а список “на модерації” не оновився; оновили банер, а показується попередній. Тому кеш — це не “увімкнути”, а спроєктувати: що саме кешуємо, як ідентифікуємо значення ключем, коли скидаємо, і як довго живе (TTL).
Починаємо з “що кешувати”. Кешують не все підряд, а те, що відповідає двом умовам: це дорого обчислювати і це часто запитують. Дорого — це або важкі SQL (агрегації, складні фільтри, кілька join, підрахунки), або зовнішні запити (API), або рендер важких блоків. Часто запитують — це головна сторінка, блоки хедера/меню, топ-новини, “популярне за добу”, сторінки категорій, результати складного пошуку, лічильники для аналітики, довідники (sections/resources) якщо вони рідко змінюються. Натомість CRUD-екрани адмінки кешувати зазвичай не треба: там важлива актуальність, а запити часто індивідуальні і з різними фільтрами.
Є три “здорові” рівні кешу. Перший — кеш довідників: списки розділів, ресурсів, стилів публікацій. Вони змінюються рідко, але читаються часто. Другий — кеш агрегатів: “скільки матеріалів у статусах за день”, “топ 10 за 24 години”, “підсумки по ресурсах”. Третій — кеш готових фрагментів сторінок (page fragments): наприклад, блок “головні новини” для конкретного ресурсу і дати. Починати найкраще з довідників і агрегатів: їх простіше інвалідувати і вони дають швидкий ефект.
Далі — ключі кешу. Ключ має однозначно описувати, що саме ви зберегли. Якщо ключ “news_list”, а всередині список новин з різними фільтрами — кеш буде повертати не те. Тому ключ завжди включає контекст. Для новинного сайту контекст часто такий: ресурс (сайт/місто), мова, розділ/категорія, тип контенту, статус (published/draft), дата/період, сторінка пагінації, сортування, та набір фільтрів. Найпростіша дисципліна: ключ = префікс + версія + “параметри”. Наприклад, “sections:v1:resource=daycom:lang=uk”. Для списків: “news_index:v1:resource=daycom:section=world:date=2026-01-03:page=1”. Важливе: не змішуйте різні типи даних в один ключ і не робіть ключі “надто загальні”.
Ще одна важлива штука — нормалізація параметрів перед ключем. Якщо фільтри приходять як масив, їх треба упорядкувати, щоб ключ був стабільним. Інакше status=draft&user=5 і user=5&status=draft дадуть різні ключі, ви отримаєте дублювання кешу. Те саме з датами: приводьте до одного формату. Це базова гігієна, яка відрізняє “кеш працює” від “кеш з’їдає пам’ять”.
Тепер інвалідація — найскладніше. Є два підходи: TTL-інвалідація (само протухає) і подійна інвалідація (скинути, коли змінили дані). TTL простий, але може брехати між моментом зміни і моментом протухання. Подійна інвалідація точніша, але її треба правильно зв’язати з подіями (створив/оновив/видалив/змінив статус). Для CMS зазвичай потрібен гібрид: TTL як страховка, і подійна інвалідація для критичних кешів (головна, категорії, блоки).
Найздоровіша подійна схема: “я змінив сутність — я скинув кеши, які від неї залежать”. Наприклад, змінили новину — треба скинути кеш її сторінки, і списки категорії/головної, де вона може бути. Змінили розділ — скинути кеш меню/довідника. Змінили банер — скинути кеш блоку банерів. Це звучить очевидно, але помилка завжди в деталях: “а де саме вона показується?”. Тому краще починати з грубішої інвалідації (скинути кеш категорії і головної), а потім, коли стане тісно, робити точніше.
Для керованості інвалідації вам потрібні теги (якщо драйвер підтримує). Теги дозволяють групувати кеш-значення і скидати групу одним викликом. Наприклад, всі кеші меню — тег menu, всі кеші новин конкретного ресурсу — тег news:resource=daycom, всі кеші категорії — тег category:world. Тоді при оновленні новини ви можете скинути news:resource=daycom або category:world. Без тегів ви будете змушені пам’ятати всі ключі, а це майже завжди закінчується тим, що кеш перестає інвалідуватися коректно.
TTL — це час життя кешу. Він потрібен навіть при подійній інвалідації як “запобіжник”: якщо ви десь забули скинути, кеш все одно протухне і система повернеться в актуальний стан. Але TTL не має бути “навмання”. Для довідників TTL може бути довгим (години або доба), бо вони рідко змінюються. Для блоків головної — коротшим (хвилини), якщо у вас часто йдуть оновлення. Для важких агрегатів “топ за добу” — 5–15 хвилин часто достатньо. Для сторінок конкретної статті, якщо її рідко редагують після публікації — TTL може бути довшим, але при редагуванні ви все одно скидаєте кеш подією. В адмінці TTL зазвичай або дуже короткий, або кеш взагалі не використовується, бо там важлива актуальність.
Критичний момент — “стампед” (cache stampede). Це коли кеш протух, і багато запитів одночасно починають перераховувати дорогий результат. Щоб цього не було, використовуйте підхід “remember” з блокуванням або хоча б рандомізуйте TTL (додавати невеликий jitter), щоб не всі ключі протухали одночасно. На старті це не завжди потрібно, але якщо у вас є сторінки з високим трафіком, це стане проблемою.
Що точно не кешувати на старті. Динамічні персоналізовані дані (для конкретного користувача) без чіткого ключа, адмінські списки з купою фільтрів, дані, які критично мають бути “прямо зараз” (наприклад, статус рахунку в процесі оплати), і все, де інвалідація не зрозуміла. Кеш без інвалідації — це повільна помилка, яка вилазить не одразу, а коли вже пізно.
Висновок: кеш — це прискорення з умовами. Кешуйте те, що дорого і часто читається; будуйте ключі з повним контекстом (ресурс/мова/фільтри/статус/сторінка); інвалідуйте подіями там, де важлива актуальність, і завжди ставте TTL як страховку. Якщо зробити це дисципліновано, кеш дає швидкість без брехні. Якщо зробити “на авось”, він стане джерелом найнеприємніших багів, які важко відтворити.