Кеширование в WordPress
Сегодня я хочу рассказать об использовании механизма кэширования в WordPress. Начну издалека.
Сторонники MT (Movable Type) часто приводят один довод в пользу «своего движка». Речь идет о том, что МТ генерирует т.н. статику. Получается, что повторная компоновка страницы не требуется и это сокращает нагрузку на сервер и ускоряет выдачу страницы браузеру.
Не вдаваясь в технические нюансы и не совсем точный термин «статика», можно сказать что по сути аргумент вполне весомый. Как ни крути, а статическая страница, которая заранее подготовленна и не требует дополнительной обработки PHP и/или запросов к базе данных, всегда будет отдаваться сервером быстрее.
Однако, если копнуть чуть глубже, то мы столкнемся с ситуацией, когда «статику» невозможно использовать. Например, если мы введем счетчик количества просмотров страницы, то получается вся страница, или хотя бы этот блок, будет динамическим. Иначе он просто перестанет работать.
С другой стороны, какой смысл генерировать заново страницу, когда она в общем-то один раз уже сформировалась и не меняется с момента публикации/редактирования?
В итоге напрашивается вывод: нужно использовать динамику и статику выборочно. Все эти размышления меня натолкнули на мысль попробовать использовать кэш WordPress'а для статики. И хотя это не 100-процентная статика (вроде html-файла), но суть точно такая же, даже лучше, поскольку можно задать время жизни кэшированной страницы.
Что такое кэш
Для начала рассмотрим базовые понятия.
Предположим у нас есть какая-то php-функция. Она вызывается каждый раз при загрузке страницы. Функция - это может быть плагин, виджет, WordPress-функция или ваша собственная. Это не важно.
function func1 ($args) {
операторы функции
echo $result; // вывод результата
}
Предположим, что наша функция выполняет обращение в БД, потом что-то еще анализирует и после выводит результат (echo). Теперь предположим, что агрументы функции не меняются или меняются редко. Получается, что результат всегда возвращается один и тот же: для первого посетителя, второго, десятого и т.д.
Вот для того, чтобы не выполнять сам блок функций, мы можем использовать кэш.
Работает это так:
function func1 ($args) {
$cache_key = 'func1'; # ключ
если кэш с $cache_key есть, то выводим его
и выходим
операторы функции
# вывод результата
echo $result;
добавляем данные в кэш с ключом $cache_key
}
Вначале мы создаем уникальный ключ для кэша, после этого проверяем есть ли этот кэш: если есть, то выводим и выходим из функции. Если нет, то выполняем функцию и в конце добавляем в кэш с нашим ключом выведенные данные.
Если мы зададим время жизни кэша, скажем 15 минут (стандарт для WordPress), то данная функция за это время не будет выполнятся - готовый результат сразу будет браться из кэша.
Функции кэша
Для работы с кэшем нам понадобятся всего две функции.
wp_cache_get() - получение кэша.
wp_cache_add() - добавление кэша.
Есть два важных параметра, которые нам следует особо оговорить: ключ и время жизни.
Ключ должен быть уникальным, иначе вместо нужных данных вы получите данные из другой функции. Правда есть один нюанс - если у вас одна или две функции, то можно было бы ключ придумать, но если функций много, то лучше этот процесс автоматизировать.
За основу можно взять функцию, возвращающую имя выполняемой функции: __FUNCTION__. Но у нас есть еще аргументы, поэтому в ключ мы должны добавить и их. Не вдаваясь в подробности, сразу скажу, что можно использовать функцию serialize(), которая возвращает переменную в специальном строковом формате.
Эти данные мы оформим в md5, чтобы получить строковое значение, состоящее из цифр и английских букв.
После этого мы можем проверить наличие кэша и если он есть, вывести его.
function func1 ($args) {
$cache_key = md5(__FUNCTION__ . serialize($args));
if ( $output = wp_cache_get($cache_key) )
return print($output);
Как видите код довольно простой и универсальный. Конечно же, если вы добавите другие аргументы, то вам нужно их указать в ключе.
Буферизация вывода
Данные кэша мы помещаем в переменную $output, но возникает вопрос, а откуда они вообще берутся? Так вот, в PHP есть возможность не выводить сразу в браузер данные (echo или print), а перед этим создать некий буфер, куда поместятся все выводимые данные. Уже после этого вывести весь буфер целиком в браузер.
ob_start(); // начало буферизации echo или print сколько нужно echo ob_get_flush(); // вывели весь буфер
Таким образом мы можем получить весь вывод, как результат функции ob_get_flush. То есть, как вы уже поняли именно его мы и будем добавлять в кэш.
Добавление данных в кэш
Код:
// время жизни кэша в секундах 60 * 3 = 3 минуты $myexpire = 60 * 3; wp_cache_add($cache_key, ob_get_flush(), '', $myexpire);
Можно не указывать $myexpire - в этом случае используется стандартные 15 минут.
Теперь объеденим все куски в единое целое.
function func1 ($args) {
$cache_key = md5(__FUNCTION__ . serialize($args));
if ( $output = wp_cache_get($cache_key) )
return print($output);
ob_start(); // начало буферизации
операторы функции (echo или print)
// время жизни кэша в секундах 60 * 3 = 3 минуты
$myexpire = 60 * 3;
wp_cache_add($cache_key, ob_get_flush(), '', $myexpire);
}
Вот такой у нас получился каркас для любой функции WordPress.
Кеширование в виджетах
В прошлых выпусках мы рассматривали виджеты и вполне логично, рассмотреть кеширование в них.
Код будет отличаться совсем не намного. Разница только в том, что виджеты, которые используют опции ($options), мы будем добавлять в ключ кэша:
$options = get_option('widget_my_widget');
$cache_key = md5(__FUNCTION__ . serialize($options));
Если функция без echo
Бывают функции, которые не выводят результат в виде echo, а возвращают значение через return. В этом случае буферизация нам не нужна, поэтому ob_start(); нужно вообще удалить, а кэш добавлять так:
wp_cache_add($cache_key, $out, '', $myexpire); return $out;
То есть результат мы помещаем в $out, которая и идет в кэш.
Контроль эффективности
Чтобы увидеть эффективность кэширования, добавьте в конец footer.php такие строчки.
<?php echo ' | MySQL: ' . get_num_queries() . '/'; timer_stop(1); ?>
Это отображает количество запросов MySQL и время выполнения. На своей тестовой машине, где включены полсотни плагинов и больше двадцати виджетов, без кэширования создается примерно 120 запросов MySQL. После включения кэша количество сокращается до 50-70, а после применения глобального кэширования - всего 10-20. Мой рекорд - 8 запросов.
Если кэш не работает
Возможна ситуация, что у вас вообще отключен кэш. Если это так, то проверьте в файле wp-config.php наличие таких строк.
# define('DISABLE_CACHE', '');
define('ENABLE_CACHE', '');
Если у вас моя сборка WordPress, то в файле приведены комментарии, так что не ошибетесь. Также у вас должен быть каталог /wp-content/cache/ и у него должны стоять права на запись. Обычно это 777.
«Глобальное» кэширование
Теперь мы подходим к самому интересному. Кэш WordPress'а так устроен, что можно кешировать практически всё. Например мы можем закешировать весь сайдбар.
В начало файла sidebar.php пишем код:
<?php
$cache_key = md5('mytheme-sidebar');
if ( $output = wp_cache_get($cache_key) )
return print($output);
ob_start();
?>
После этого идет код сайдбара, а в конце файла выводим кэш:
<?php $myexpire = 60; // время жизни кэша в секундах wp_cache_add($cache_key, ob_get_flush(), '', $myexpire); ?>
Таким образом наш сайдбар будет генерироваться раз в 60 секунд.
Кэширование основных текстов
Сейчас мы сделаем то, чем так гордятся MT-шники.
Признаться я долго не мог придумать каким образом сделать ключ для основного текста (тот, что в цикле TheLoop). Привязываться к номеру записи, или рубрики или еще чему-нибудь неэффективно, потому что потребуется переделка шаблона. Решение пришло неожидано и оно оказалось простым и изящным: ключ нужно генерировать на основе $wp_query (подробности и продолжение). Эта переменная содержит структуру (массивы и объекты) из которых и выводятся данные в цикле TheLoop.
Итак, перед
<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
Мы добавляем:
<?php $cache_key = md5(serialize($wp_query)); if ( $output = wp_cache_get($cache_key) ) return print($output); ob_start(); ?>
А в конце уже привычный:
<?php $myexpire = 60 * 3; // время жизни кэша в секундах 60 * 3 = 3 минуты wp_cache_add($cache_key, ob_get_flush(), '', $myexpire); ?>
Вуаля! Вот мы получили «статику» на WordPress.
Что нельзя кэшировать
Один пример я уже привел - счетчики, которые сразу же отображают результат. Наверное можно было бы как-то исхитриться и выводить их, скажем через js или iframe. Или попробовать как-то разделить кэширование на части, чтобы отображать статику из кэша, а динамику геренировать отдельно. Тут все зависит от многих вещей, поэтому универсального способа нет.
Бессмысленно кэшировать простой html-код. Например счетчики-рейтинги или рекламу. То есть в кеш нужно добавлять только тот код, который:
- PHP;
- создает запросы к БД;
- выполняет сложные/длительные вычисления.
Ну и конечно же бессмыслено делать кэш для малопосещаемого сайта. По моим прикидкам, кэширование оптимально где-то от 60-100 хитов в час.
Возможные проблемы при работе с кэшем
Собственно это даже не проблемы, а особенность. Например, вы разрабатывате функцию или добавляете её в свой шаблон. Но, поскольку ключ кэша не изменился, то вы сразу не увидите и изменения. Выход очень простой - отключить кэш на время отладки. Для этого раскоментируйте строчку «define('DISABLE_CACHE', '');» в wp-config.php. Этим самым вы отключите кэш.
Также возможны проблемы при комментировании - если вы закешировали страницу целиком, вместе с комментариями, то новые комментарии появятся только после обновления кэша. Поэтому подходить к этому нужно осторожно.
Постоянная ссылка: http://maxsite.org/?p=276
Версия для печати
RSS: Кеширование в WordPress

