Кеш: Как 3 функции значительно ускорят ваш сайт

У меня на сайте достаточно давно была проблема загрузки главной страницы – из-за отображения большого количества записей из многих рубрик.

Представьте: основные рубрики в табах – это по 7 записей в 5 категориях (где каждая категория – отдельный запрос). Вкладка “Остальное” – это 4 рубрики по 8 записей в каждой (где каждая рубрика тоже отдельный запрос). С учетом того, что WordPress при использовании new WP_Query и/или get_posts делает не по одному запросу, а больше, то получается весьма значительное число – больше 60 запросов к базе.

Естественно, это значительно замедляло загрузку страницы. Генерация была на уровне 5 секунд и кол-во общих запросов – 83.

Из-за того, что у меня на VDS от Avihost стоит не Apache, а чистый Nginx, то большинство плагинов кеширования у меня просто не работало. И я решил написать свою собственную систему частичного кеширования.

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

И я написал 3 функции, которые:

  1. получает из базы, формирует html и возвращает тот контент, который мне нужен;
  2. делает сброс кеша и создает новый при сохранении/создании любой записи в админке;
  3. делает проверку при загрузке страницы свежести кеша, и если он “свежий”, то показать его, в ином случае – см. пункт 2.

Всего 3 функции, которые уменьшили кол-во запросов с 83 до 19 и время генерации страницы с 5 секунд до 0.550.

Так как мой код строго завязан на своем контенте, я покажу вам костяк, который вы сможете доработать и использовать у себя.

Итак, для начала создайте в папке шаблона папку cache/, а в ней пустой файл cache.txt.

Далее определяете тот блок контента, который вы хотите кешировать (преположим, это будет  список записей). Копируете весь этот код из home.php (к примеру) в отдельный временный файл, вместо него в home.php вставляете функцию

<?php cd_home_content() ?>

Потом открываете файл functions.php шаблона. И вставляете туда следующее:

function cd_home_get_content(){
	ob_start();
	// здесь ваш код получения записей в лупе, который вы скопировали во временный файл из home.php
	$data = ob_get_clean();
	return $data;
}
// Заново генерируем кеш, если обновили какой-то пост
add_action('save_post', 'cd_home_cache_flush');
function cd_home_cache_flush($post_id){
	$file = dirname(__File__). '/cache/cache.txt';
	$data = cd_home_get_content();
	if (is_writable($file)) {
		// я убрал здесь проверки на то, записался ли кеш или нет. У меня все записывается, потому оставил как есть
		$handle = fopen($file, 'w+');
		fwrite($handle, $data);
		fclose($handle);
	}
	if( ! is_admin())
		return $data;
	else
		return $post_id;
}
// Непосредственно вывод контента - или из кеша, или только что сгенерированного
function cd_home_content(){
	// Готовим переменные
	$file = dirname(__File__). '/cache/cache.txt';
	$last_mod = filemtime($file);
	if ( $last_mod < ( time() - 86400 ) ) { // наш кеш устарел - прошло больше 24 часов с момента его генерации, делаем все заново
		echo '<!--[Cache: Creating cache for '.time().'...]-->';
		echo cd_home_cache_flush($file);
	}else{ // все в порядке, пользуемся кешом
		echo '<!--[Cache: From cache '.$last_mod.']-->';
		echo file_get_contents($file);
	}
}

Собственно, по комментариям в коде все понятно, я надеюсь.

86400 – это сутки в секундах, через это время кеш будет принудительно сбрасываться, если вы ничего не публиковали/не изменяли на сайте. Эта проверка необязательна, но я решил оставить.

Минус такого способа кеширования – долгое время сохранения поста. Но так как это в админке происходит и пользователи ничего не замечают, то я не считаю такую проблему не критичной.


Внедрил тоже самое – но для блока комментариев и ключевых слов в сайдбаре. Точно также сделал 3 функции, а обработчик кеша (сбрасыватель и генератор) повесил на add_action( 'wp_insert_comment', 'cd_sidebar_cache_flush', 20, 2);

Все отлично работает – убрал еще 3-4 запроса (стало 15) и время генерации упало до 0.413 сек.

комментария 13

  1. Копируете весь этот код из home.php (к примеру) в отдельный временный файл

    Вот это не совсем ясно. В какой-такой временной файл? куда его ложить, с каким расширением и как обозвать?

    И для новичка непонятно как быть когда кешируемых выводом несколько. Какие части кода для functions.php надо добавить для других выводов.

    • Новички либо пользуются плагинами, либо сами пытаются разобраться опытным путем)

    • Вы копируете этот код, чтобы потом использовать в cd_home_get_content(). Никуда не надо его сохранять, понимайте мои слова как скопировать в буфер обмена.

      А если несколько, то лучше всего для каждого блока создавать 2 функции cd_xxx_get_content() и cd_xxx_content(), где вторая используется в шаблоне для вывода, а первая – для генерации контента. Также надо будет внести именения в cd_xxx_cache_flush(), чтобы сбрасывался и заново генерировался кеш для другого блока.

  2. И для новичка непонятно как быть когда кешируемых выводом несколько. Какие части кода для functions.php надо добавить для других выводов.

    Я бы попробовал создать еще одну функцию, только с другим названием.

  3. Вмеcто вызова 2 функций:

    $data = ob_get_contents();
    ob_end_clean();

    можно вызывать только 1:

    $data = ob_get_clean();

  4. Ради интереса внедрил кеширования для блока комментариев в сайдбаре. Этот комментарий отчасти является тестом обновления кеша :)

  5. Ха, только что обнаружил, что спам тоже сбрасывает кеш сайдбара :)

    • Jettochkin:

      )))))))))))))))

      на самом деле ob_ – не очень хорошее решение… во всяком случаи в связке WP+BP вызывает иногда проблемы.. и если вы предлагаете “клиенту” разный контент (к примеру, вошел пользователь или нет) – то будут косяки)

      можно просто сохранять результат работы – в текстовый файл в папке cache и затем выдавать клиентам..

      • Так у меня так и сделано – результат работы… А возможность регистрации и авторизации я никому не даю :) Я же для своего сайта писал это решение. Теперь вот думаю посмотреть в сторону memcache…

  1. 12.05.2011

    […] До этого я облегчил главную страницу, внедрив частичное кеширование результатов запроса к мускулу (кстати, спасибо Юрию […]

  2. 12.10.2011

    […] использование частичного кеширования кусков сайта (как это сделать); […]

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *