WordPress как на ладони
Недорогой хостинг для сайтов на WordPress: wordpress.jino.ru

Оптимизация производительности WordPress за счет постоянных ссылок (практика)

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

Суть метода

Так как нам нужно избавиться от постоянных генераций ссылок, то логично сгенерировать ссылку и сохранить её где-нибудь, а потом просто её от туда "брать". Сохранять я решил в Базе Данных, в которой, как оказалось, есть даже поле для этого - guid. Впрочем, guid создан не совсем для этого и разработчики не рекомендуют менять guid он создается один раз при публикации поста и затем уже не меняется.

Поле guid таблицы wp_posts создано для хранения там уникального значения - идентификатора записи. Нужен он для идентификации записи в RSS ленте. GUID так и расшифровывается: Globally Unique Identifier - глобальный уникальный идентификатор. По этому полю парсеры RSS определяют обрабатывали они запись или нет. Именно поэтому разработчики не рекомендуют менять это поле, вообще никогда, даже если ваш сайт переехал на другой домен. Если это поле изменить, то ваши читатели RSS, могут получить кучу уже опубликованных материалов.

Но, так как я с детства не любил овал ис детства угол рисовал, думаю на это предупреждение закрыть глаза и изменять guid - записывать туда постоянную ссылку. Аргументация такая: сейчас RSS пользуются не многие и ссылка эта измениться если переезжать на другой домен или поменять ЧПУ, а это происходит редко, и даже если изменить, ваши читатели увидят последние 10 постов в своем RSS, которые уже были - думаю это не совсем не страшно...

Удобство постоянной генерации заключается в том, что если у нас по каким-то причинам измениться постоянная ссылка на статью, то генерирующиеся ссылки всегда будут оставаться рабочими (актуальными), тогда как ссылки которые записываются, при изменении, могут стать нерабочими. Однако, на деле ссылки практически никогда не меняются, а если уж мы сменили домен сайта или сменили структуру ЧПУ у статей, то перезаписать (создать новые) ссылки в БД очень просто (я сделал мини плагин).

Поле куда мы будем записывать "готовые" постоянные ссылки находится в таблице wp_posts (в ней хранятся все записи) и называется guid. Это поле нигде не используется - только для RSS см. выше. Рассчитано оно как раз для того, чтобы туда записывать готовые permalink-и - туда пишутся иногда готовые пермалинки, а иногда ссылки вида http://example.com/?p=133, причем происходит это в каком-то хаотичном порядке, зависит, вроде, от того публикуется материал до или после записи первого черновика. Да, это и не важно.

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

  1. При публикации/обновлении записи в поле guid таблицы wp_posts записывать постоянную ссылку на статью

  2. При использовании ссылки в шаблоне WordPress не использовать функцию get_permalink(), а брать уже готовую ссылку: $post->guid.

Реализация

Чтобы в БД правильно записывались постоянные ссылки в поле guid, я сделал такой хак, для файла темы functions.php:

add_action( 'save_post', 'guid_write', 100 );
function guid_write( $id ){
	if( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE  ) return false;

	if( $id = intval($id) ){
		global $wpdb;
		$wpdb->update( $wpdb->posts, ['guid'=>/*wp_make_link_relative*/( get_permalink($id) ) ], ['ID'=>$id] );
	}

	clean_post_cache( $post_id );
}

Этот хак работает когда пост/страница публикуется, обновляется. Хак так же срабатывает при удаленной публикации через xml-rpc.

Таким образом, на вновь создаваемый сайт нужно вставить этот хак в файл темы functions.php и затем в шаблоне, в циклах вывода постов использовать не

<?php echo get_permalink() ?>

а

<?php echo $post->guid ?>

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

echo get_permalinks();
echo get_permalinks($post->ID);
the_permalink();
the_permalink($post->ID);

на

echo $post->guid

Важно понимать, что, например, конструкции вида the_permalink(25); или the_permalink($post_id); , где $post_id устанавливается заранее, как ID какого-то поста, менять на echo $post->guid нельзя, потому что такие конструкции получают ссылки на определенный пост, а echo $post->guid выводит ссылку на пост, который в текущий момент находится в глобальной переменной $post.

Кстати, в моих функциях вывода: вывод последних записей, предыдущие из категории также можно и даже нужно заменить get_permalink($pst->ID) на $pst->guid. Я в них специально добавлял в выборку поле guid. Однажды даже забыл поменять на get_permalink() и в комментариях мне припомнили, что функция не чувствительная к пермалинкам smile

Для уже рабочих сайтов, где поля guid уже определены "неправильно"

Если сайт уже рабочий и в нем уже немало записей, постоянные ссылки которых, записаны "неправильно" в поле guid, то для таких случаев я сделал плагин на перезапись (ремонт) поля guid:

Ремонт guid

Скачать плагин

Fix Guid V1.2
Плагин для просмотра/редактирования поля guid в базе данных WordPress.
Скачано: 146, размер: 2.3 KB

Устанавливается плагин как обычно: копируем файл из архива fix_guid.php в каталог плагинов и активируем его в админке.

После того как поля guid "отремонтированы", плагин можно удалить. Плагин так же умеет грамотно удалять ревизии записей (удаляется все включая авто-сохранения).

Рекомендации

Если у вас структура ЧПУ не содержит тегов %category%, %tag%, %author%, то нет острой необходимости использовать этот прием оптимизации. Однако, если эти теги используются, то этот прием даст отличный эффект.

Тесты

В комментариях меня "спровоцировали" провести тесты, чтобы посмотреть как в действительности влияет на оптимизацию такой способ, вот что получилось:

С ЧПУ вида /%category%/%postname% функция the_permalink() (она же get_permalink(), только без фильтров) затрачивает 0,12 секунды на генерацию 10 ссылок. Тогда как при ЧПУ вида, например, /%year%/%monthnum%/%day%/%postname% уходит всего 0,02 секунды.

Замеры я проводил на своем компе, где, для сравнения, главная страница WordPress 3.0.1 с дефолтной темой генерируется за 1,3 секунды.

Таким образом, если у нас в ЧПУ присутствует %category% и на странице выводиться, скажем 50 уникальных (не повторяющихся) ссылок, то на генерацию этой страницы уйдет плюс 0,6 секунды (0,12*5=0,6), почти пол секунды.

Нужно отметить (я этого раньше не знал), что один раз сгенерировав ссылку WordPress, её кэширует и при повторном вызове the_permalink() для того же поста, ссылка "генерируется" в доли секунды. Имею ввиду это:

//получаем ссылку для поста с ID 25
the_permalink(25); //время 0,012
//получаем ссылку еще раз
the_permalink(25); //время, примерно, 0,0001

Вроде, все сказал, что хотел, все остальное можно без труда прояснить в комментариях smile

28 комментов
Полезные 1 Вопросы 1 Все
  • @ alexpts my-wordpress.ru

    Насколько быстрее сайт стал работать? По-моему прирост в производительности совсем мал. Генерация ссылки на лету занимает очень мало времени.

    Можете дать технические замеры "до и после"?

    Причем тут есть наверное один тонкий момент, это переадресация. Если человек стал использовать ЧПУ на сайте, то по старым ссылкам вида "?p=id" будет происходить редирект и как бы все хорошо. Если же перебить напрямую, то теряется гибкость определенная.

    С одной стороны это логично, но что-то мне говорит, что это повлияет только на время генерации страницы и очень незначительно, что овчинка выделки не будет стоить, но все равно интересно взглянуть на технические замеры smile

    Ответить9.6 лет назад #
    • Kama7774

      если же перебить напрямую

      Редирект разве от этого зависит? То что я предлагаю, это всего лишь не использовать get_permalink(), который выводит ссылку на самой странице. А по какой ссылке посетитель пришел на сайт и как движок редиректит при этом - это другое. Хотя, может я не понял что имелось ввиду?

      что овчинка выделки не будет стоить

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

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

      Вот, нашел, но не совсем то - это с выключенным кэшем, на странице около 120-140 вызовов ссылок, ЧПУ /%category%/%year%-%month%/%post_name%, версия ВП 2.9.1, на локалке:
      до: SQL: 1280 за 8.256 сек., 15.39 MB
      после: SQL: 493 за 3.517 сек., 15.24 MB

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

      Ответить9.6 лет назад #
      • @ alexpts my-wordpress.ru/blog

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

        В штатном режиме будет одинаковый показатель.

        Ответить9.5 лет назад #
  • @ alexpts my-wordpress.ru

    Встроенный кэш по умолчанию включен, думаю, что не стоит на это совсем смотреть.

    А примерно на вскидку разнуца будет примерно такая:
    до: SQL: 25 за 3.617 сек., 15.27 MB
    после: SQL: 25 за 3.517 сек., 15.24 MB

    Из-за такого прироста что-то делать вручную - просто лень.

    Ответить9.6 лет назад #
    • Kama7774

      Как-нить протестирую, выложу сюда результаты.

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

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

      Ответить9.6 лет назад #
    • Kama7774

      Спасибо за проявленный интерес к статье thank_you

      Протестировал на генерацию 10 ссылок уходит 0,12 секунды.

      Дополнил пост, подробнее там.

      Ответить9.6 лет назад #
      • @ alexpts my-wordpress.ru/blog

        Это опять все зависит от настроек сервера.

        У меня на локальном хосте домашнего ПК страница целиком генерится за 0,2 сек. А на локальном хосте ноутбука эта же страница вся генерится за 0,09 сек

        Ответить9.5 лет назад #
        • Kama7774

          Ну да, но сравнительная нагрузка то остается. Само сабой разумеется, чем мощнее сервер и лучше настроен, тем меньше необходимости что-то оптимизировать...

          Ответить9.5 лет назад #
  • VolAnd twitter.com/VolAnd_ru

    Назрела необходимость исследования производительности WP для выявления "слабых мест".
    Kama, не подскажешь, как проводил стресс-тесты?
    Можно ли на локалке эмулировать "посетителей" с различными параметрами?

    Ответить9.5 лет назад #
    • Kama7774

      Такое эмулирование я не проводил ни разу. Помочь не могу в этом вопросе.

      Ответить9.5 лет назад #
  • Евгений

    WordPress не рекомендует использование /%category%/%postname%/ и в первой части статьи вы доступно объяснили, почему. Но там же вы пишете, что при использовании кэша количество запросов к БД составляет 0.
    Я небольшой знаток WordPress, поэтому не совсем понял, почему кэширование не является оптимальным (а из ваших слов я понял, что кэширование только частично решает проблему).
    И чисто технический вопрос: в каком именно шаблоне находится то, что нужно заменять? Поиском по файлам я нашел 3-4 десятка файлов, в которых находятся выражения, подлежащие замене.

    Ответить9.4 лет назад #
    • Kama7774

      Замену нужно производить в файлах темы.

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

      Если можно, то лучше просто не используйте /%category%/ в ссылке.

      Ответить9.4 лет назад #
  • Otshelnik-Fm233 otshelnik-fm.ru

    По оптимизации - Kama, у вас на блоге jquery.lightbox.min.js.gzip - грузится 3 раза...

    Опять же - много запросов идут от смайлов.

    и no_avatar.gif грузится столько раз сколько пользователей без авторизации с граватара (сейчас 3)(причем 1 раз только сервер отдал её с 304 ответом, а 2 картинки грузятся последовательно и без кэша).еще один из no_avatar.gif - грузился 3.5 секунды...

    Это относится к оптимизации, но не к этой статье, так что смело удаляйте мое сообщение. Я не обижусь smile

    Ответить9.4 лет назад #
    • Kama7774

      jquery.lightbox.min.js.gzip - грузится 3 раза

      Это вы как смотрели, где? Не вижу в упор. Да и быть такого не может, чтобы один и тоже скрипт грузился 3 раза! Максимум, браузер заголовками обменивается с сервером на предмет, надо ли еще раз загружать.

      no_avatar.gif - не может грузиться несколько раз. Там только редиректит с gavatar на файл no_avatar.gif, на редирект время уходит, а сама картинка 1 раз загружается, но это уже издержки использования gavatar.

      Ответить9.4 лет назад #
  • Otshelnik-Fm233 otshelnik-fm.ru

    я фаербагом смотрю. в фаерфоксе. вкладка - сеть
    http://s008.radikal.ru/i304/1103/bf/604e1f8f55eb.jpg
    http://s43.radikal.ru/i101/1103/85/44dba2fc488d.jpg
    Нет. именно отдает несколько раз.

    Ответить9.4 лет назад #
    • Kama7774

      Спасибо за скрины и проявленный интерес! Но я опять не соглашусь, все происходит так как я и сказал.

      Вы с цветами разберитесь - там не загрузка, а ожидание показано (редиректы, обмен заголовками, блокировка). А размер указан для всех элементов без исключения, не важно загружался он или нет.

      Спрятанные смайлики кстати не грузятся, блокируются, там и это показано.

      Ответить9.4 лет назад #
  • Шаман

    Спасибо за функцию, действительно прирост в скорости есть. В категориях и на морде. Ссылка на скачивание кстати некорректная.

    Ответить7 лет назад #
  • fartova9

    Доброго времени суток!
    Не подскажите, почему в guid вбиты не только постоянные ссылки, но еще пути к изображениям (название сайта.ру/wp-content/uploads/2012/02/***.png)?

    Ответить6.5 лет назад #
    • Kama7774

      Для вложений, медиафайлов, там будет ссылка на этот самый файл.

      Ответить6.5 лет назад #