ГлавнаяРазноеКэширование объектов в WordPress

Кэширование объектов в WordPress

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

Что такое кэширование объектов

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

Чтобы понять принцип работы кэша объектов в WordPress, рассмотрим простой пример функции, которая выполняет запрос в базу данных:

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

Если вызвать эту функцию несколько раз подряд, то каждый раз мы будем выполнять запрос в базу данных:

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

Функцию foo() можно легко ускорить с помощью кэширования объектов в WordPress, поместив результат выполнения запроса в кэш следующим образом:

function foo() {
    $cache = wp_cache_get( 'foo' );
    if ( $cache )
        return $cache;

    $value = $wpdb->get_results( "SELECT ..." );

    wp_cache_add( 'foo', $value );
    return $value;
}

Функции для работы с кэшем объектов и их параметры мы рассмотрим чуть позже. Здесь важно понять, что первая часть функции ищет сохраненное значение в кэше объектов, и выдает его если оно найдено. Если значения в кэше не оказалось, то мы его получаем с помощью запроса в базу данных, сохраняем в кэш и возвращаем результат.

Таким образом, только первое обращение к функции foo() выполнит запрос в базу данных, а последующие обращения вернут результат существенно быстрее с помощью кэша:

foo(); // SQL!
foo(); // cache
foo(); // cache

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

<title><?php echo get_option( 'blogname' ); ?></title>
...
<h1>Добро пожаловать на <?php echo get_option( 'blogname' ); ?></h1>
...
<p>&copy; 2014 <?php echo get_option( 'blogname' ); ?></p>

Если ваш ответ «один», то принцип работы кэширования объектов вы усвоили. Тем не менее, правильным ответом здесь является «ноль» из-за особенности работы опций с кэшем объектов в WordPress. Подробности мы рассмотрим в конце статьи.

Основные функции кэширования объектов

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

wp_cache_get()

Функция wp_cache_get() читает значение из кэша объектов по заданному ключу. Если значение с таким ключом не существует, функция wp_cache_get() возвращает false. Параметры функции:

  • $key — ключ по которому выполняется поиск
  • $group — группа в которой производится поиск, если группа не задана подразумевается группа по умолчанию «default», параметр не обязательный

Примеры:

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

В функции wp_cache_get() есть также два дополнительных параметра, которые используются крайне редко.

Параметр $force позволяет выполнять принудительный поиск, в обход локального кэша объектов, при работе с внешним кэшированием объектов (подробнее о внешнем кэшировании в следующем разделе).

Параметр $found принимает переменную по ссылке, и устанавливает ее в значение true или false, в зависимости от того, было ли найдено значение в кэше или нет. Этот параметр полезен в том случае, когда значение, которое хранится в кэше объектов может быть булевым false.

wp_cache_add()

Функция wp_cache_add() добавляет значение в кэш объектов по заданному ключу. Если значение с таким ключом уже существует в кэше объектов WordPress, то функция wp_cache_add() ничего не делает и возвращает false. Параметры функции:

  • $key — уникальный ключ, по которому значение может быть найдено
  • $data — значение сохраняемое в кэш объектов
  • $group — группа, которая позволяет использовать один и тот же ключ в разном контексте, значение по умолчанию — «default», параметр не обязательный
  • $expire — максимальное время жизни значения в кэше, используется только в некоторых плагинах внешнего кэширования объектов, параметр не обязательный

Например:

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

wp_cache_set()

Функция wp_cache_set() похожа на wp_cache_add() и принимает те же самые параметры. Она сохраняет заданное значение в кэше объектов WordPress. Единственным отличием является то, что функция wp_cache_set() установит значение в кэш, даже если оно уже существует. Если значения в кэше нет, то wp_cache_set() его добавит.

Пример:

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

wp_cache_replace()

Функция wp_cache_replace() тоже похожа на wp_cache_add() и wp_cache_set(), и принимает те же самые параметры. Функция wp_cache_replace() заменяет существующее значение в кэше объектов. Она отличается тем, что если значение не найдено в кэше объектов, новое значение функция добавлять не будет.

Например:

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

wp_cache_delete()

Функция wp_cache_delete() удаляет значение из кэша объектов по заданному ключу. Параметры те же, что и у функции wp_cache_get(). Функция вернет true если значение было найдено и удалено, иначе false. Пример:

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

wp_cache_incr() и wp_cache_decr()

Функции wp_cache_incr() и wp_cache_decr() позволяют инкрементировать или декрементировать значения в кэше объектов. Эти функции особенно полезны при работе с какими-либо счетчиками. Примеры:

wp_cache_incr( 'key' );
wp_cache_incr( 'key', 2 ); // увеличить на 2
wp_cache_incr( 'key', 1, 'my-group' );

wp_cache_flush()

Функция wp_cache_flush() сбрасывает весь кэш объектов WordPress. Пользоваться этой функцией стоит с особой осторожностью, поскольку она может стать причиной пониженной производительности WordPress.

Постоянство кэша объектов

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

На первый взгляд такой кэш не очень полезен, но если посчитать например, сколько раз за один запрос WordPress выполняет функцию get_option(), то выгода от кэширования объектов становится очевидной.

Так же в WordPress есть возможность использовать внешнее хранилище для кэша объектов, например сервер Memcached или APC, при этом кэш объектов в WordPress становится постоянным. Это значит что при следующем обращении к вашей странице, ранее закэшированные значения становятся доступными.

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

Внешний/постоянный кэш объектов (external/persistent object cache) в WordPress реализуется исключительно в виде плагина, например:

Данные плагины позволяют использовать серверы Memcached, APC или Redis соответственно, для кэширования объектов в WordPress. При использовании внешнего кэширования нужно так же иметь представление о том, сколько памяти потребуется для ваших самых частых объектов, чтобы правильно сконфигурировать сервер-хранилище.

Если вы разрабатываете плагин для WordPress, в котором поведение зависит от наличия внешнего кэширования объектов, воспользуйтесь функцией wp_using_ext_object_cache():

if ( wp_using_ext_object_cache() ) {
    wp_cache_set( 'key', 'value' );
} else {
    update_option( 'key', 'value' );
}

В данном простом примере мы сохраняем значение в кэше объектов только если кэш является внешним и постоянным. Если плагин для внешнего кэширования объектов отсутствует, то будет использоваться опция.

При работе с плагинами внешнего кэширования объектов появляется новое понятие «локальный кэш объектов». Это значит, что при повторном запросе одного и того же ключа, объектный кэш не будет делать повторный запрос на сервер Memcached, а выдаст значение из локального кэша.

Некоторые плагины внешнего кэширования реализуют параметр $force с помощью которого можно повлиять на это поведение.

Группы

У большинства функций кэширования объектов есть параметр $group, который позволяет использовать один и тот же ключ в разном контексте.

Теоретически это лишь дополнительный префикс к ключу, но на практике группы имеют большое значение при работе с WordPress в режиме сети (мультисайт) или при использовании плагинов для внешнего кэширования объектов.

В ядре WordPress используется несколько разных групп, например:

  • default — группа по умолчанию
  • posts — группа для хранения записей, страниц и произвольных типов записей
  • options — группа для хранения опций
  • comment — группа для комментариев
  • themes и plugins — группы для хранения тем и плагинов соответственно
  • users — группа для хранения информации о пользователях

Группы в WordPress делятся на глобальные и не глобальные, а так же на постоянные и непостоянные.

Глобальные группы

Глобальные группы в WordPress имеют значение только при работе с WordPress в режиме сети. По умолчанию кэш объектов добавляет префикс с идентификатором текущего сайта (blog id) к каждому ключу в кэше объектов.

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

Самым очевидным примером глобальной группы в WordPress является группа «users», поскольку пользователи в WordPress берутся из одной глобальной таблицы, независимо от текущего сайта в сети. Другими примерами глобальных групп являются: «themes», «blog-details», «site-options», «site-transient».

При желании в список глобальных групп вы можете добавить и собственную группу с помощью функции wp_cache_add_global_groups():

wp_cache_add_global_groups( 'my-global-group' );

Постоянные группы

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

wp_cache_add_non_persistent_groups( 'my-group' );

Таким образом, при работе с плагином внешнего кэширования, например Memcached Object Cache, и при сохранении значений в группе «my-group», значение будет сохранено только в локальном кэше объектов, и не будет сохраняться на сервер Memcached.

В ядре WordPress по умолчанию определено несколько непостоянных групп: «comment», «counts», «themes» и «plugins». Все остальные группы, включая группу «default» в WordPress являются постоянными.

Отключение кэша объектов

Когда вам необходимо получить данные с помощью встроенных функций WordPress, но при этом не сохранять полученные данные в кэш, вы можете на время отключить добавление в кэш с помощью функции wp_suspend_cache_addition(). Это может быть полезным например при импорте большого количества данных:

wp_suspend_cache_addition( true ); // отключить добавление в кэш объектов
// импорт...
wp_suspend_cache_addition( false ); // включить добавление в кэш

Подобным образом с помощью функции wp_suspend_cache_invalidation() можно отключить возможность сброса кэша, правда на данный момент эта функция работает только для кэша записей, и только при попытке его сбросить с помощью функции clean_post_cache().

Объектный кэш и опции

Опции в WordPress имеют особое отношение с кэшем объектов. Это легко посмотреть в реализации функции get_option(), которая при самом первом обращении вызовет встроенную функцию wp_load_alloptions(), которая в свою очередь загрузит все опции с пометкой autoload в базе данных WordPress.

Первый раз функция get_option() вызывается в ядре WordPress еще до того, как загрузится наша активная тема, и именно поэтому при обращении к get_option() для того, чтобы получить название сайта, описание и прочее, запросов в базу данных никаких не будет:

echo get_option( 'blogdescription' );
if ( get_option( 'comments_open' ) )
    printf( 'На сайте %s комментарии открыты', get_option( 'blogname' ) );

echo 'Свяжитесь с нами по адресу: ' . get_option( 'admin_email' );

Данный код не выполнит ни одного запроса в базу данных WordPress.

По этой причине разработчикам тем и плагинов WordPress не стоит бояться таких функций как get_option(), bloginfo(), или get_post_meta() (использует подобный подход к кэшированию), а советы «убрать обращения к get_option() для того, чтобы ускорить сайт» можно смело игнорировать.

Заключение

Кэш объектов является одним из самых интересных API в WordPress, и понимать принципы его работы должен каждый разработчик тем и плагинов. Для более детального изучения всех внутренних функций кэширования объектов советуем взглянуть на файл wp-includes/cache.php.

В отладке проблем с кэшем объектов в WordPress поможет популярный плагин Debug Bar.

Работа с кэшем объектов в Debug Bar

Работа с кэшем объектов в Debug Bar

В Debug Bar можно посмотреть статистику обращений в кэш объектов и потребляемую память каждой группой, а при наличии внешнего кэширования объектов, Debug Bar покажет каждое обращение к внешнему серверу.

Если у вас возникнут вопросы при работе с кэшированием объектов в WordPress — оставьте комментарий и мы обязательно вам ответим.

Подписаться на рассылку

Подписаться → Подпишитесь на бесплатную рассылку журнала WP Magazine и получайте новости, события, подборки тем и плагинов, уроки, советы и многое другое в мире WordPress!

  • MrClon

    Ядро WP использует кеш объектов только для оптимизации обращений к БД? Или там так-же хранятся результаты каких-то вычислений?
    Есть-ли смысл настраивать постоянный кеш объектов если оптимизировать работу с БД не требуется?

    P.S. выпилите уже этот disqus!

    • Нет, в ядре WordPress кэш объектов нацелен именно на ускорение работы с объектами из базы данных. Если у вас база данных быстрее чем, скажем Memcached (в чем я сомневаюсь), то вам постоянный кэш не нужен.

      P.S. выпилите уже этот disqus!

      Воспользуйтесь ссылкой «Отключить Disqus» в конце страницы.

  • MrClon

    mysql конечно не быстрее nosql-хранилища, но всё-же его вклад во время генерации страницы недостаточно высок что-бы заморачиваться чем-то подобным.
    Особо головоломные вещи я и так кэширую (хотя скорее это предкомпиляция) своими костылями, они ближе к логике сайта и потому довольно эффективны.

  • Юрий Рассадин

    Константин, а есть готовое решение для удаления/сброса всего кеша (используется плагин Memcached Object Cache) только для отдельного блога в multisite режиме wp?

    • Если вы не знаете конкретные ключи, можно их удалить через wp_cache_delete(), а групп в Memcached вообще нет, а проходить через список всех ключей в кэше и пытаться понять их группу это слишком тяжело будет. В вашем случае можно придумать что-то вроде индивидуального идентификатора для каждого блога и добавлять его к каждому ключу. А когда нужно «сбросить» то просто меняйте идентификатор на новый, иными словами — соль.

      • Юрий Рассадин

        Да, пожалуй попробую добавить соль к blog_id и через нее сбрасывать. Благодарю за идею. Сам склонялся к удалению ключей по id, но их достаточно много и не всегда заранее известны, а перебор всех ключей совсем плохой вариант.