﻿---
title: Кэширование объектов в WordPress
description: "Полный разбор WP_Object_Cache API: wp_cache_get/set/add/replace/delete, группы (глобальные и непостоянные), persistent object cache через Redis/Memcached, особенности опций и autoload."
---

## Что такое объектный кэш

Кэширование объектов (object cache) — встроенный механизм ядра WordPress, который сохраняет данные произвольного типа и отдаёт их при повторных обращениях. Используется для хранения результатов сложных операций: запросов к базе, вычислений, внешних вызовов.

Пример без кэша — каждый вызов функции бьёт в базу:

```php
function foo() {
    global $wpdb;
    return $wpdb->get_results( "SELECT ..." );
}

foo(); // SQL!
foo(); // SQL!
foo(); // SQL!
```

Та же функция с объектным кэшем:

```php
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(); // cache
foo(); // cache
```

В ядре объектный кэш используется практически для всего: опции, записи, страницы, метки, категории, пользователи. Именно поэтому повторные вызовы `get_option()` или `get_post()` в одном запросе не создают лишних запросов к базе.

## Основные функции

Кэш реализован классом `WP_Object_Cache`, но работать с ним удобнее через вспомогательные функции.

### wp_cache_get()

Читает значение по ключу. Если ключ не найден — возвращает `false`.

```php
$value = wp_cache_get( 'key' );
$value = wp_cache_get( 'key', 'my-group' );
```

Два редких дополнительных параметра:

- `$force` — принудительный поиск во внешнем хранилище в обход локального кэша;
- `$found` — переменная по ссылке, становится `true`/`false` в зависимости от того, найдено ли значение. Полезно, когда в кэше может лежать легитимный `false`:

```php
$value = wp_cache_get( 'key', 'default', false, $found );
if ( ! $found ) {
    // Ключа точно нет в кэше — регенерируем
}
```

### wp_cache_add()

Добавляет значение по ключу. Если ключ уже существует — **ничего не делает** и возвращает `false`.

```php
$added = wp_cache_add( 'key', 'value' );
$added = wp_cache_add( 'key', 'value', 'my-group' );
```

Параметры: `$key`, `$data`, `$group` (по умолчанию `default`), `$expire` (время жизни — работает только в некоторых persistent-плагинах).

### wp_cache_set()

Те же параметры, но **перезаписывает** значение, если оно уже есть. Если значения нет — добавляет.

```php
wp_cache_set( 'key', 'value', 'my-group' );
```

### wp_cache_replace()

Заменяет **только существующее** значение. Если ключа нет — ничего не добавляет.

```php
wp_cache_replace( 'key', 'new-value', 'my-group' );
```

Логика трёх функций: `add` — только если нет; `set` — в любом случае; `replace` — только если есть.

### wp_cache_delete()

Удаляет значение по ключу. Вернёт `true`, если значение найдено и удалено.

```php
$deleted = wp_cache_delete( 'key', 'my-group' );
```

### wp_cache_incr() и wp_cache_decr()

Инкремент и декремент числовых значений — для счётчиков:

```php
wp_cache_incr( 'key' );              // +1
wp_cache_incr( 'key', 2 );           // +2
wp_cache_incr( 'key', 1, 'my-group' );
wp_cache_decr( 'key' );              // -1
```

### wp_cache_flush()

Сбрасывает **весь** объектный кэш. Использовать с особой осторожностью: на нагруженном сайте полный сброс вызывает всплеск запросов к базе.

## Постоянство кэша

По умолчанию объектный кэш в WordPress **непостоянный**: значения живут только в рамках одного запроса, при следующем запросе кэш пуст.

Это не бесполезно: за один запрос WordPress вызывает `get_option()` около 500 раз — кэш в памяти схлопывает их до одного обращения к базе.

### Внешний (постоянный) объектный кэш

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

- [Redis Object Cache](https://wordpress.org/plugins/redis-cache/) — самый популярный сегодня, с экраном диагностики в админке;
- [Memcached Object Cache](https://wordpress.org/plugins/memcached/) — классика для highload;
- [WordPress Redis Backend](https://github.com/ericmann/wordpress-redis-backend) — альтернативная реализация для Redis.

Технически это drop-in `object-cache.php` в `wp-content/`. При выборе хранилища учитывайте, сколько памяти потребуется самым частым объектам — и конфигурируйте сервер с запасом.

Проверка наличия внешнего кэша в коде плагина:

```php
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`.

Добавить свою глобальную группу:

```php
wp_cache_add_global_groups( 'my-global-group' );
```

### Непостоянные группы

Постоянные группы — те, что пишутся во внешнее хранилище при наличии плагина. По умолчанию постоянны все группы; исключить группу можно так:

```php
wp_cache_add_non_persistent_groups( 'my-group' );
```

Значения такой группы сохраняются только в локальном кэше PHP и не уходят в Redis/Memcached. Непостоянные группы ядра: `comment`, `counts`, `themes`, `plugins`.

## Отключение кэша

При импорте большого объёма данных добавление каждого элемента в кэш — лишняя работа. Отключить добавление на время:

```php
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()` вызывается ядром ещё до загрузки темы. Поэтому такой код не выполняет ни одного запроса к базе:

```php
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](https://wordpress.org/plugins/debug-bar/). При наличии внешнего кэша Debug Bar покажет каждое обращение к внешнему серверу.

Для глубокого изучения механизма смотрите исходник `wp-includes/cache.php` в ядре.

## Материалы и источники

- [Кэширование объектов в WordPress](https://wpmag.ru/2014/keshirovanie-obektov-wordpress/) — Константин Ковшенин, WP Magazine
- [Основы кэширования в WordPress](https://wpmag.ru/2013/keshirovaniye-wordpress/) — Константин Ковшенин, WP Magazine
- [Transients API](https://wpcraft.ru/kb/stacks/wordpress/cache/transients.md) — транзитное кэширование с TTL
- [Кэширование WordPress: инфраструктурный уровень](https://wpcraft.ru/kb/stacks/wordpress/cache/server-level-cache.md) — OPcache, Varnish, Nginx FastCGI