Кэширование объектов в WordPress
Что такое объектный кэш
Заголовок раздела «Что такое объектный кэш»Кэширование объектов (object cache) — встроенный механизм ядра WordPress, который сохраняет данные произвольного типа и отдаёт их при повторных обращениях. Используется для хранения результатов сложных операций: запросов к базе, вычислений, внешних вызовов.
Пример без кэша — каждый вызов функции бьёт в базу:
function foo() { global $wpdb; return $wpdb->get_results( "SELECT ..." );}
foo(); // SQL!foo(); // SQL!foo(); // SQL!Та же функция с объектным кэшем:
function foo() { global $wpdb;
$cache = wp_cache_get( 'foo' ); if ( $cache ) { return $cache; }
$value = $wpdb->get_results( "SELECT ..." ); wp_cache_add( 'foo', $value ); return $value;}
foo(); // SQL!foo(); // cachefoo(); // cacheВ ядре объектный кэш используется практически для всего: опции, записи, страницы, метки, категории, пользователи. Именно поэтому повторные вызовы get_option() или get_post() в одном запросе не создают лишних запросов к базе.
Основные функции
Заголовок раздела «Основные функции»Кэш реализован классом WP_Object_Cache, но работать с ним удобнее через вспомогательные функции.
wp_cache_get()
Заголовок раздела «wp_cache_get()»Читает значение по ключу. Если ключ не найден — возвращает false.
$value = wp_cache_get( 'key' );$value = wp_cache_get( 'key', 'my-group' );Два редких дополнительных параметра:
$force— принудительный поиск во внешнем хранилище в обход локального кэша;$found— переменная по ссылке, становитсяtrue/falseв зависимости от того, найдено ли значение. Полезно, когда в кэше может лежать легитимныйfalse:
$value = wp_cache_get( 'key', 'default', false, $found );if ( ! $found ) { // Ключа точно нет в кэше — регенерируем}wp_cache_add()
Заголовок раздела «wp_cache_add()»Добавляет значение по ключу. Если ключ уже существует — ничего не делает и возвращает false.
$added = wp_cache_add( 'key', 'value' );$added = wp_cache_add( 'key', 'value', 'my-group' );Параметры: $key, $data, $group (по умолчанию default), $expire (время жизни — работает только в некоторых persistent-плагинах).
wp_cache_set()
Заголовок раздела «wp_cache_set()»Те же параметры, но перезаписывает значение, если оно уже есть. Если значения нет — добавляет.
wp_cache_set( 'key', 'value', 'my-group' );wp_cache_replace()
Заголовок раздела «wp_cache_replace()»Заменяет только существующее значение. Если ключа нет — ничего не добавляет.
wp_cache_replace( 'key', 'new-value', 'my-group' );Логика трёх функций: add — только если нет; set — в любом случае; replace — только если есть.
wp_cache_delete()
Заголовок раздела «wp_cache_delete()»Удаляет значение по ключу. Вернёт true, если значение найдено и удалено.
$deleted = wp_cache_delete( 'key', 'my-group' );wp_cache_incr() и wp_cache_decr()
Заголовок раздела «wp_cache_incr() и wp_cache_decr()»Инкремент и декремент числовых значений — для счётчиков:
wp_cache_incr( 'key' ); // +1wp_cache_incr( 'key', 2 ); // +2wp_cache_incr( 'key', 1, 'my-group' );wp_cache_decr( 'key' ); // -1wp_cache_flush()
Заголовок раздела «wp_cache_flush()»Сбрасывает весь объектный кэш. Использовать с особой осторожностью: на нагруженном сайте полный сброс вызывает всплеск запросов к базе.
Постоянство кэша
Заголовок раздела «Постоянство кэша»По умолчанию объектный кэш в WordPress непостоянный: значения живут только в рамках одного запроса, при следующем запросе кэш пуст.
Это не бесполезно: за один запрос WordPress вызывает get_option() около 500 раз — кэш в памяти схлопывает их до одного обращения к базе.
Внешний (постоянный) объектный кэш
Заголовок раздела «Внешний (постоянный) объектный кэш»Постоянный кэш реализуется плагинами, которые подменяют хранилище на внешнее:
- Redis Object Cache — самый популярный сегодня, с экраном диагностики в админке;
- Memcached Object Cache — классика для highload;
- WordPress Redis Backend — альтернативная реализация для Redis.
Технически это drop-in object-cache.php в wp-content/. При выборе хранилища учитывайте, сколько памяти потребуется самым частым объектам — и конфигурируйте сервер с запасом.
Проверка наличия внешнего кэша в коде плагина:
if ( wp_using_ext_object_cache() ) { wp_cache_set( 'key', 'value' );} else { update_option( 'key', 'value' );}С внешним кэшем появляется понятие локального кэша: при повторном запросе ключа в рамках одного запроса страницы плагин не дёргает Memcached/Redis повторно, а отдаёт значение из памяти PHP. Параметр $force в wp_cache_get() позволяет обойти это поведение.
Параметр $group позволяет использовать один и тот же ключ в разном контексте — фактически это префикс ключа. Группы имеют значение при работе в режиме Multisite и с внешними хранилищами.
Группы ядра: default, posts, options, comment, themes, plugins, users.
Глобальные группы
Заголовок раздела «Глобальные группы»В Multisite к каждому ключу добавляется префикс с ID сайта (аналог префиксов таблиц wp_3_posts, wp_4_posts). Глобальные группы — исключение: они общие для всей сети.
Классический пример — users: пользователи хранятся в одной глобальной таблице независимо от сайта. Другие глобальные группы ядра: themes, blog-details, site-options, site-transient.
Добавить свою глобальную группу:
wp_cache_add_global_groups( 'my-global-group' );Непостоянные группы
Заголовок раздела «Непостоянные группы»Постоянные группы — те, что пишутся во внешнее хранилище при наличии плагина. По умолчанию постоянны все группы; исключить группу можно так:
wp_cache_add_non_persistent_groups( 'my-group' );Значения такой группы сохраняются только в локальном кэше PHP и не уходят в Redis/Memcached. Непостоянные группы ядра: comment, counts, themes, plugins.
Отключение кэша
Заголовок раздела «Отключение кэша»При импорте большого объёма данных добавление каждого элемента в кэш — лишняя работа. Отключить добавление на время:
wp_suspend_cache_addition( true ); // отключить добавление в кэш// ... импорт тысяч записей ...wp_suspend_cache_addition( false ); // включить обратноПохожая функция wp_suspend_cache_invalidation() отключает сброс кэша, но работает только для кэша записей при вызове clean_post_cache().
Объектный кэш и опции
Заголовок раздела «Объектный кэш и опции»Опции имеют особое отношение с кэшем. При первом вызове get_option() ядро выполняет wp_load_alloptions() — одним запросом загружает все опции с флагом autoload в кэш.
Первый get_option() вызывается ядром ещё до загрузки темы. Поэтому такой код не выполняет ни одного запроса к базе:
echo get_option( 'blogdescription' );if ( get_option( 'comments_open' ) ) { printf( 'На сайте %s комментарии открыты', get_option( 'blogname' ) );}echo 'Свяжитесь с нами: ' . get_option( 'admin_email' );Практический вывод: не бойтесь get_option(), bloginfo() и get_post_meta() (использует такой же подход). Советы «убрать обращения к get_option() для ускорения» — миф, основанный на непонимании кэша.
Следствие для архитектуры: опции с autoload = yes загружаются при каждом запросе. Не складируйте в autoload-опции большие массивы данных — это замедляет каждый запрос.
Отладка
Заголовок раздела «Отладка»Статистику обращений к кэшу и потребление памяти по группам показывает плагин Debug Bar. При наличии внешнего кэша Debug Bar покажет каждое обращение к внешнему серверу.
Для глубокого изучения механизма смотрите исходник wp-includes/cache.php в ядре.
Материалы и источники
Заголовок раздела «Материалы и источники»- Кэширование объектов в WordPress — Константин Ковшенин, WP Magazine
- Основы кэширования в WordPress — Константин Ковшенин, WP Magazine
- Transients API — транзитное кэширование с TTL
- Кэширование WordPress: инфраструктурный уровень — OPcache, Varnish, Nginx FastCGI