Под нагрузкой : реализация зависимостей кэшируемых данных через метки в кэше

По службе потребовалось реализовать дублирование наиболее часто используемых данных из СУБД в более быстром кэше memcached в приложении на базе фреймворка Yii. Две сложности, с которыми столкнулся — это поддержка релевантности кэша при удалении записей из базы данных и реализация этого в условиях балансировки нагрузки.

В базе есть внешние ключи между таблицами и при строк одной таблицы можно автоматически удалять строки других таблиц. В реализации кэша в Yii тоже есть возможность связать данные между собой с помощью так называемых «зависимостей» (Dependency).

В документации перечислены следующие виды зависимостей:

  • CFileCacheDependency: зависимость меняется, если изменилось время последней модификации указанного файла.
  • CDirectoryCacheDependency: зависимость меняется, если изменился любой файл в каталоге или в подкаталогах.
  • CDbCacheDependency: зависимость меняется, если изменился результат запроса некоторого определённого SQL-выражения.
  • CGlobalStateCacheDependency: зависимость меняется, если изменилось значение определённого глобального состояния. Глобальное состояние — это переменная, являющаяся постоянной в многократных запросах и сессиях приложения. Её значение устанавливается с помощью методаCApplication::setGlobalState().
  • CChainedCacheDependency: зависимость меняется, если изменилась любая зависимость цепочки.
  • CExpressionDependency: зависимость меняется, если изменился результат определённого PHP выражения.

Все они не подошли мне потому что приложение распределено на несколько серверов (load balancing) и общими ресурсами у них являются только базы данных и сервера memcached. В поисках решения я наткнулся на статью «Теггирование кеша в Yii» на хабрахабре. Решение мне уже известное, но в данном случае адаптация под Yii.

Принцип реализации кэширования с тегами

Данные для хранения в кэше сопровождаются списком тегов. При сохранении данных проверяется, есть ли в кэше ранее сохранённое значение каждого тега. Если нет — генерируется и сохраняется новое. Список имён тегов и их текущих значений попадает в кэш вместе с данными.

При извлечении данных из кэша значения каждого тега сравнивается с текущим. Если текущее значение не известно или не соответствует, данные считаются невалидными. Значение тега можно удалить. Ну и конечно, он может быть удалён автоматически из кэша, даже если ему установить бесконечное время жизни — как известно, наличие данных в кэше не гарантируется.

Решение автора не оптимальное: слишком много запросов в кэш при активной работе приложения. Я ввёл упрощение — за миллисекунды обработки одного запроса значения тегов в кэше не могут измениться извне, а только внутри процесса. Это позволило ввести статический кэш в памяти для хранения текущих значений тегов. Кроме того, многие реализации кэшей поддерживают одновременный запрос нескольких значений из кэша, а при отсутствии такой возможности она имитируется. Поэтому при извлечении данных из кэша все её теги можно проверить одним запросом. Фактически будет два запроса в кэш — чтение данных + чтение всех тегов.

Код расширения yii-cache-tag-dependency

Код с примерами и тестами выложен на github: https://github.com/pvolyntsev/yii-cache-tag-dependency

Вопросы по расширению задавайте здесь в комментариях или в github/Issues.

Нужны ли тебе рекомендации по использованию тегов для кэшируемых данных? В проектах какого рода ты хотел бы применить их?

 

Полезные ссылки:

 

 

 

Павел Волынцев

Уже более 15 лет занимаюсь разработкой веб-проектов. Fullstack Senior Developer. IT евангелист — доношу свет знаний об информационных технологиях. Профессиональные цели: Дать людям возможность дать людям больше.

Читайте также: