Мой сайт о WordPress и PHP
 
Rss2Email
31 августа 2007

Кеширование в 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. Этим самым вы отключите кэш.

Также возможны проблемы при комментировании - если вы закешировали страницу целиком, вместе с комментариями, то новые комментарии появятся только после обновления кэша. Поэтому подходить к этому нужно осторожно.

google.com bobrdobr.ru del.icio.us technorati.com linkstore.ru news2.ru rumarkz.ru memori.ru moemesto.ru

20 комментариев к “Кеширование в WordPress”

  1. Ю.Б.:

    :cool:

  2. Nick:

    Полезно и элегантно! Спасибо, будем пользоваться :idea:

  3. Maksus:

    Ниасилил...попробую с утречка!

  4. Эдуард:

    А вот я так и не осилил этот кэш. В результате живу без него :(
    Как только я включаю кэш, сразу начинают появляться новые файлы в папке /wp-content/cache/
    Они появляются и появляются, постепенно их становится очень много, один раз эта папка разрослась до 200 мегабайт! Экперименты не помогли, так и отключил :(

  5. saahov:

    Как пользователь Movable Type могу сказать (смайл), что статика, которую по-другому и не назовёшь — это не единственный довод в пользу MT. Хотя он, бузусловно, очень сильный.

    Немного более подробно про него мы говорили в блоге Султана Салпагарова.

  6. GTAlex:

    Имхо задумываться о таких вещах стоит только когда сервак уже затрещит от нагрузки, а мне такое пока не грозит :(

  7. Alexander:

    Спасибо, будем экспериментировать :smile:

  8. Vyazovoi:

    Очень полезная информация

    Недавно тоже разбирался с кешированием, решил отключить до того момента пока не подниму посещаемость потомучто блог находится все ещё в процессе активной разработки и кеш только мешает

  9. kirizoid:

    Гораздо более эффективно использовать Wp-Cache2 plugin (http://mnm.uib.es/gallir/wp-cache-2/). Могу сказать, что это реально помогает.

    Блог, с кол-вом трафика ~100-200К на wordpress'е без кэширования создаёт нагрузку где-то 4-6уе; при использовании плагина - нагрузка падает до значения меньше единицы.

    Очень хороший плагин - рекоммендую всем людям, кроме тех, кто использует всякие трейд-скрипты для обмена траффика, а-ля AT3. Вся проблема из-за того, что в шаблон нужно вставлять этот грёбаный PHP virtual(). Хотя это можно обойти использую bash, но это уже костыль.

    Удачи! :cool: :cool:

  10. kirizoid:

    Неплохой совет по совершенствованию Wp cache2 можно найти на блоге AskApache.com : http://www.askapache.com/wordpress/wp-cache-speed-hack.html

    Чекайте :arrow:

  11. Evgen:

    Наконец то. Спасибо! Из - за отсутствия полноценной инфы по кэшу вордпресса собрался переходить на другую CMS. Переход отменяется!
    Осталось "победить" редактор в админке и к WP вообще ни каких претензий не будет.
    Ещё раз спасибо.

  12. Михаил:

    kirizoid, мы не могли бы пообщаться на эту тему плотней в ICQ? У меня как раз блог под 100К начинает заваливаться, сервер очень мощный, канал ещё мощней, встроенный кэш судя по всему работает, плагин который ты советуешь ставил, с ним хуже получается, стоит ATX, но тем не менее папка cache исправно наполняется. Судя по трафу одно дело делаем :) , стукни если не затруднит 191/052*482

  13. Max'S:

    Хм, попробуем плагин :)

    Для перехода на КМС с блога рекомендую Джумлу. Кстати кэш в ней встроеный, а еще есть компонент для кэширование полных страниц.

    О джумле можете почитать на сайте Joomla! Україна

    Сам юзаю джумлу паралельно с водпресом, но ее для порталов или просто больших сайтов.

    З.Ы.: Автору сайта большой респэкт за идею сопоставления друпала, джумлы, вордпреса (тип, тоже в алфавитном порядке ;) )

  14. oldvovk:

    Интересно. Сейчас проэкспериментируем с глобальным.

    >Осталось "победить" редактор в админке и к WP вообще ни каких >претензий не будет.

    Чего его побеждать. Поставил себе Windows Live Writer или Blog Jet - и пиши без проблем из оффа а потом грузи в блог.

  15. Florid:

    Я использую плагин "WP-Cache 2.0" для кэширования страниц на своем Блоге. Замечательный плагин. Удобный интерфейс. Гибкие настройки.

  16. Atari:

    Florid,
    а не возникает ли проблем сплагином кеширования при работе с сейпом (sape) и прочими системами, когда нужно установить сторонний php код ?

  17. Maxa:

    При использовании данного хака пропадает подвал и сайдбар, даже если их закешировать

  18. oldvovk:

    Atari: Проблема с sape и сторонними вполне вероятна.

    Так же как вероятна при использовании глобального кеширования.
    Бот sape оказался не в состоянии найти данные при кешировании.

  19. Охотник на зелёных:

    Вот в прошлый раз я как то уже изучал эту информацию и мне особо это дело не было нужно, а вот теперь время пришло и для меня тоже актуален кэш. И прочитав второй раз у меня возникло сразу несколько вопросов.
    1. А вот по умолчанию, если я включаю кэш в файле конфига, то кэш как работает? Что кэшируется? Как узнать?
    2. Народ говорил, что разрастается папка cache и я помню у меня была подобная проблема. Это в связи с чем она разрастается? Можно ли как то уменьшить её объём? Это как то регулируется. А то 200MB это всё-таки уже не шутки, с учётом того, что BD всего лишь 10MB.
    3. Вот это вот ваши хуки объяснения как что закешировать, они параллельно со стандартным кэшированием (я про п.1) происходят или нет?

  20. Белый:

    Круто конечно, но можно по пунктам, а? :wink: ну не силен я так в PHP как для этого текста, к своему стыду, стараюсь изучать но пока "читать" так не научился к сожалению. Ранее я видел в рассылках (сейчас не найдя этой статьи) было подробное объяснение, как и что делать в WP 2.1 для того, чтобы работал там кеш, сейчас же у меня WP 2.3.3 и тоже очень хотелось бы снизить нагрузку на сервер, но как это сделать? :idea: Пожалуйста сделайте версию для действий или разъясните подробнее. Заранее спасибо!


Оставьте комментарий! (Вы согласны с правилами)

 

:mrgreen: :neutral: :twisted: :arrow: :shock: :smile: :???: :cool: :evil: :grin: :idea: :oops: :razz: :roll: :wink: :cry: :eek: :lol: :mad: :sad: :!: :?:

При добавлении кода (html, php) заменяйте < на &lt; и > на &gt;.
Внимание: антиспам - зверь! Копируйте своё сообщение перед отправкой. На всякий случай.