Системный администратор
0,0
рейтинг
2 декабря 2011 в 12:29

Администрирование → Всесторонняя оптимизация сайта на WordPress

Уважаемые жители Хабра!

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

Поехали


Мы начали с исследования причин торможения сайта. Лично мне больше всего нравится сервис LoadImpact. Не так давно у него появились новые фичи и новый интерфейс, и сам сервис стал называться «LoadImpact 2.0». Все это сказано не рекламы ради — сервис действительно хорош. Также потестировать сайт можно с помощью аналогичных сервисов — Pingdom Full Page Test и Google PageSpeed.

С помощью Load Impact Page Analyzer мы обнаружили большое значение параметра Time to first byte (4 секунды), который рассказал нам о том, что PHP-скрипты, формирующие страницу, выполняются слишком долго. Нужно было искать самые тормозные компоненты сайта.

Добавив в шаблон footer.php строчку

<!-- <?php echo get_num_queries(); ?> queries -->

мы увидели, что для генерации главной страницы требуется 82 SQL-запроса, а это очень много. Стало ясно, что нужно сокращать количество модулей WordPress, а вместе с ними и количество SQL-запросов. Поразбиравшись с установленными плагинами WordPress, которых было около 40 штук, мы поняли, что все-таки большинство из них действительно нужны.

Решили сделать профилирование всего PHP-кода в шаблоне. Мы не стали заморачиваться с Xdebug + Performance Testing (для этого нужно было ставить кучу стороннего софта), а провели простое самописное профилирование вида:

<!-- HEADER01 - <?php print microtime(true) ?> -->

У нас сразу обнаружились следующие основные проблемы:
  • модуль рекламы с геотаргетингом
  • модуль, отображающий курс акций Apple
  • боковые блоки «Обзор приложений» и «Самое интересное»
Каждая из этих проблем при ближайшем рассмотрении вызывает эталонный facepalm. Сейчас расскажу о каждой из них.

Раз


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

if ($ip_part)
{
    for ($y1=$ip_part[0]['start'];$y1<=$ip_part[0]['end'];$y1++)
    for ($y2=$ip_part[1]['start'];$y2<=$ip_part[1]['end'];$y2++)
    for ($y3=$ip_part[2]['start'];$y3<=$ip_part[2]['end'];$y3++)
    for ($y4=$ip_part[3]['start'];$y4<=$ip_part[3]['end'];$y4++)
    {
        $ip[] = $y1.".".$y2.".".$y3.".".$y4;
        if ($ip_addr == $y1.".".$y2.".".$y3.".".$y4) return true;
    }
}

В этот момент мне даже стало немного жалко процессор, который все это время выполнял пустую работу…

Исходные диапазоны IP-адресов Санкт-Петербурга задавались заранее в массиве $ip_part. Как видно из этого куска кода, при каждом обращении к функции зачем-то создавался динамический массив $ip и заполнялся строковыми (!) данными с IP-адресами, тупейшим образом через 4 вложенных цикла. В каждой итерации цикла IP-адрес посетителя сравнивался построчно (!) с получившейся строкой IP-адреса. Пофиксить это было просто:

$ip_tmp = explode(".",$ip_addr);
if ( $ip_part )
    if ( ($ip_part[0]['start'] <= $ip_tmp[0]) and ($ip_tmp[0] <= $ip_part[0]['end']) )
    if ( ($ip_part[1]['start'] <= $ip_tmp[1]) and ($ip_tmp[1] <= $ip_part[1]['end']) )
    if ( ($ip_part[2]['start'] <= $ip_tmp[2]) and ($ip_tmp[2] <= $ip_part[2]['end']) )
    if ( ($ip_part[3]['start'] <= $ip_tmp[3]) and ($ip_tmp[3] <= $ip_part[3]['end']) )
        return true;

Не отрицаю, что этот код можно переписать и более оптимально, однако и после этой правки сайт заработал на 2 секунды быстрее!

Два


Второй плагин под названием Embedded Stock Data, который у нас на страничке показывал котировки Apple. Обнаружилось, что этот гад при каждой загрузке страницы ходил на другой сайт (!) через CURL. Естественно, это сильно увеличивало время загрузки страницы. Решили проблему естественным образом — на сервере раз в минуту по крону вызывался скрипт, который скачивал нужную котировку и сохранял ее в специальный файл. Далее мы этот файл инклудили. Это дало нам около 500 мс.

Три


Боковые блоки «Обзор приложений» и «Самое интересное» также выполнялись ощутимое время. Заглянув внутрь шаблона (sidebar.php), я увидел классическую ошибку начинающих веб-программистов под названием «ORDER BY RAND()». Подробнее о сути этой проблемы можно, например, почитать тут.

Для интересующихся, был вот такой код:

$args=array(
    'caller_get_posts'=>'1',
    'post__not_in' => $sticky,
    'cat'=>'75',
    'fields'=>'ids',
    'post_per_page'=>'20',
    'orderby'=>'ID',
);
$sk_count = new WP_Query($args);

$args=array(
    'caller_get_posts'=>'1',
    'post__not_in' => $sticky,
    'showposts'=>'4',
    'cat'=>'75',
<b>    'orderby'=>'rand'</b>
);
$sk_posts = new WP_Query($args);

В нем мы заменили выделенную строку на

    'post__in'=>array_rand(array_flip($sk_count->posts),4)

Суть решения — мы предварительно выбираем 20 самых свежих записей, потом выбираем из этих 20 записей случайные 4 штуки, а затем уже выбираем из базы посты с этими номерами.

На этом мы сэкономили еще полсекунды.

Переезд на Clodo


Наш железный сервер изначально хостил большое количество сайтов, но самым главным по трафику и по значению был AppleInsider.ru. Нам захотелось обезопасить этот сайт от соседства с другими сайтами, и было решено перенести его в облако компании Clodo. Главным преимуществом Clodo можно назвать «эластичность» тарификации — сколько ресурсов используешь, столько и платишь. Положа руку на сердце, также хочется отметить техподдержку компании, которая даже в не-VIP варианте вела себя корректно, отвечала на все наши каверзные вопросы и помогала собственно в настройке.

«Под шумок» с переносом были сделаны некоторые изменения, например мы убрали насовсем тяжелый веб-сервер Apache, а вместо него сделали связку nginx + php-fpm, которая кушает меньше памяти. Nginx работал по обычной схеме — отдавал статику, а все остальное перенаправлялось к скрипту index.php, в котором идет дальнейшая WordPress-логика.

Чтобы не отнимать ресурсы железа, отведенного под веб-сервер nginx + php-fpm, мы вынесли сервер базы данных MySQL на отдельный сервер, связав его с Web-сервером посредством локальной сети.

Также мы настроили два отдельных «облачных» сервера — для нашего подкаста AppleInsider.ru и Яблочного огрызка и для текстовых онлайн-трансляций. О них мы расскажем подробнее в следующий раз.

Переход на Clodo ненамного сократил время генерации страницы, зато это обезопасило сам сайт.

WP Super Cache и асинхронная реклама


На этом этапе страница у нас формировалась примерно за 1 секунду. Уже неплохо, но нам было мало, ведь оставалось еще несколько моментов, которые можно было оптимизировать.

Например, воспользоваться плагином для кеширования страниц WordPress'а под названием WP Super Cache. Работает как обычный кеш — пользователь заходит на страницу, она при этом генерируется и кладется в каталог /wp-content/cache/supercache. Следующему посетителю будет отдаваться уже статический заранее сформированный HTML-файл. Казалось бы, все супер — сайт летает…

Однако, как вскоре обнаружилось, использование кеширования поломало геотаргетированную рекламу. Проверка на вхождение адреса посетителя в диапазон адресов Санкт-Петербурга выполнялось на уровне PHP, т.е. уже за кешем. Соответственно, результат этой проверки «навеки» оставался в кеше. А нам нужно было все-таки разграничивать показ баннеров.

Решили эту задачу следующим образом — в шаблоне странички в нужных местах были расставлены Javascipt-инструкции, указывающие на то, что в этот div-блок нужно подгрузить контент с определенного URL-а. Таким образом, пользователь беспрепятственно получает закешированную страницу с Javascript-инструкциями, и уже после появления первых букв начинают загружаться рекламные и прочие структурные блоки сайта, хранящимся по отдельным специальным URL-адресам. Визуально это происходит очень быстро. Так нам удалось достичь двух целей — во-первых, за счет кеширования значительно ускорился сайт, и во-вторых, получилась корректно работающая геотаргетированная реклама.

ClodoStorage


Последний шаг, который мы сделали в оптимизации — перенесли статический контент (картинки, стили CSS, Javascript) на CloudStorage. Подробнее о нем можно почитать тут. В двух словах — это Content Delivery Network от компании Clodo, а по-русски — очень быстрый сервер, специально заточенный для раздачи статики.

Интеграция с WordPress состоит в правильной настройке двух плагинов — CDN Sync Tool (для Clodo нужно использовать специальную сборку библиотеки PHP Cloud Files API) и WP Super Cache.

Первый плагин, CDN Sync Tool, занимается тем, что заливает контент WordPress на хранилище, производя попутно с этим самым контентом какие-нибудь действия. При наличии плагина WP Minify он может сжимать CSS- и JS-файлы. Также он может налету сжимать загружаемые картинки. При этом используется библиотека GD, для нас наиболее приемлемым оказался уровень сжатия GD Compression Level = 2 — выражаясь в терминах других программ, это 90% качества. Самое главное, конечно, это то, что при стандартной загрузке картинок авторами, они стали автоматически заливаться на хранилище.

Для статики мы завели отдельный поддомен static.appleinsider.ru, а первоначальную загрузку контента проделывали с помощью CyberDuck — это довольно удобная программа для работы с FTP, SFTP, WebDAV, Cloud Files, Google Docs и Amazon S3.

Вот скриншот плагина CDN Sync Tool, тюнингованного под Clodo (кликабельно):



Второй плагин, о котором мы уже упоминали, WP Super Cache, поступает хитро — он берет сформированную страницу и заменяет в ней все URL-ы для статических файлов на URL в хранилище. Например, http://www.appleinsider.ru/Angry-Birds.jpg станет http://static.appleinsider.ru/Angry-Birds.jpg

Итоги




Сейчас при отсутствии страницы в кеше страница загружается за 1.25 секунды, при наличии в кеше — 250 мс — и это с учетом всех 4 факторов — DNS Lookup + Connection + Time to first + Download. Я считаю, что это потрясающий результат!

В заключение приведу сухие факты — статистику использования ресурсов и финансовые затраты.

За неделю работы 23.11.2011 — 30.11.2011:

Web




Scale Server — ресурсы оперативной памяти 45708.00 GB*min 457.080 руб.
Scale Server — ресурсы процессора 1027116.50 CPU sec 427.300 руб.
Использование дисковых ресурсов 5760.00 GB*hour 57.600 руб.
Входящий трафик (полных гигабайт) 34 GB 6.800 руб.
Исходящий трафик (полных гигабайт) 129 GB 129.870 руб.
Использование диска для резервных копий 1910.00 GB*hour 19.100 руб.
SMS уведомления 1 шт. 5.000 руб.
Всего списано со счета 1102.75 руб.


DB




Scale Server — ресурсы оперативной памяти 22924.00 GB*min 229.240 руб.
Scale Server — ресурсы процессора 67490.34 CPU sec 28.080 руб.
Использование дисковых ресурсов 3840.00 GB*hour 38.400 руб.
Входящий трафик (полных гигабайт) 1 GB 0.200 руб.
Исходящий трафик (полных гигабайт) 12 GB 12.000 руб.
Использование диска для резервных копий 1910.00 GB*hour 19.100 руб.
Всего списано со счета 327.02 руб.


CloudStorage



Cloud Storage — использование диска 1139.00 GB*hour 11.390 руб.
Cloud Storage — входящий трафик (полных гигабайт) - не тарифицируется
Cloud Storage — исходящий трафик (полных гигабайт) 330 GB 330.000 руб.
Всего списано со счета 341.39 руб.

Всего за неделю работы сайта — 1771.16 руб. Довольно много, но стабильность и довольные пользователи стоят того :-)

Очень жду ваших комментариев.
Александр Тарасов @oioki
карма
25,2
рейтинг 0,0
Системный администратор
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Администрирование

Комментарии (31)

  • +3
    Хорошая статья про оптимизация wordpress. Видимо в наших presets сайтов добавим некоторый полезности, описанные выше.
  • +1
    Хорошая статья. Очень не хватает подобных про оптимизация VPS под Wordpress.
    • +5
      Неважно, виртуальный у вас сервер, VPS или железный сервер, 80% эффекта для сайта на Wordpress можно добиться, просто поставив плагин кеширования — WP Super Cache или Hyper Cache
  • НЛО прилетело и опубликовало эту надпись здесь
  • +10
    Статья хорошая, но все же… заголовок-то: "всесторонняя оптимизация сайта на вордпресс". А по факту в чем оптимизация? Правка 3rd-party плагинов, установка wp_cache, перенос в облако. То есть это хорошо, но оптимизация самого вордпресса-то где?:)
    • +1
      Оптимизация в том, что сайт стал открываться не за 4 секунды, а за четверть (по замерам loadimpact).
      Естественно, что мы пользовались какими-то существующими плагинами и более продвинутыми аппаратными возможностями. Сам WordPress хакать смысла нет, ведь это ядро CMS, а как правильно это ядро использовать зависит от конкретного сайта. Как мне кажется, для такого насыщенного контентом сайта как наш, мы воспользовались им правильно.
  • +4
    Очень удивился, что нет ничего про eAccelerator, я как-то для себя делал сравнение с ним и без и ещё до кучи разницу между Apache и Nginx по скорости в одних и тех же условиях на вордпресе. Если кому интересно мог бы постом оформить. Скажу сразу на одном ядре до оптимизации 6 запросов в секунду после 20-30.
    • +3
      Интересно конечно.
  • +1
    Нужная статья! В избранное (c WP дело не имел, но вдруг случится). А неоправданно высоким количеством запросов к базе грешат почти все популярные фреймворки, не только WP. Ибо за лень всегда приходится чем-то расплачиваться.
  • 0
    >Сейчас при отсутствии страницы в кеше страница загружается за 1 секунду

    И это очень и очень много. У вас так-же происходит подмена понятий. PHP отдается и генерируется != страница загружается.
    • 0
      действительно. поправил в статье.
  • +6
    > Углубившись в модуль, я обнаружил вот такой, простите, говнокод:
    Ваш код тоже мягко говоря шедевр…
    IP адрес удобно сравнивать если предварительно переобразовать его в число. Причём это можно сделать сразу в БД.
    • +1
      спасибо, я ждал подобной критики) исправим
  • +6
    Спасибо.

    Мы ovkuse.ru держим на WordPress 56 000 уников. Новогодняя пара наше время — новогодние рецепты. Ожидаем 200к/сутки.

    В январе напишу как мы оптимизировали WP.
  • +1
    Кому интересно, есть свежая статья журнала Smashing Magazine о интеграции WP с Amazon S3:
    Integrating Amazon S3 With WordPress
  • +2
    У Расмуса Лердорфа есть презентация PHP Performance, где он препарирует WordPress и показывает, что можно с ним сделать. В том числе с помощью HipHop for PHP.
  • +1
    Вот таким должен быть партизанский маркетинг :) Ненавязчивое упоминание бренда, и куча полезной инфы по смежной теме. Но это к вашему материалу никак не относится.
    • +2
      Мы читали много о проблемах у Clodo.ru
      Но за месяц на их серверах, у нас был один момент, когда на Blade серверах Cloud Storage сгорело питание.
      Вопрос решили быстро.

      Хотя соглашусь, такой ПР компании Clodo.ru полезен, слишком много на них числится косяков.
  • 0
    Еще, как вариант, можно подгружать часть инфы с сайдбара через ajax. Например, облако тегов — все таки большой кусок информации, который можно подгрузить после загрузки всей страницы, и так далее :).
  • +1
    1. Серверная оптимизация — на четворочку, не больше
    WP к сожалению плохо оптимизирован, а вы по сути убрали легкие 10%
    Как минимум можно и нужно убрать кучку запросов на старте — там наверняка запросы на отсутствующие параметры и вообще множественный лишний геморрой
    Плагины — это боль. Большая часть вообще жертвы абортов и требуют переписывания или замены на альтернативы.

    2. статика и домены
    Вы начали не с того конца. Начинать надо с уничтожения или слияния лишних запросов. У вас 17 скриптов и 7 css. У вас туча мелкой графики. Простая канкатенация, спрайты или data:uri дали бы намного лучший эффект чем вынос на отдельный домен, хотя при большом количестве графики в содержимом это тоже не плохо.

    3. клиентская оптимизация
    На самом деле все описанное выше — не такая уж важная борьба за дурацкие миллисекунды, тогда как намного важнее просто что-то показаться клиенту пока сайт грузиться себе. Конечно загрузка первого html важно, но получив его браузер продолжает показывать индикатор загрузки вместо отображения сайта — этого у вас просто нет.

    • +1
      А забыл добавить немного данных померянных на моем блоге
      там есть замечательная гистограмка

      по которой видно как меняется загрузка в зависимости от используемого метода.
      • 0
        Красивые и наглядные графики.
  • +1
    Стоит объединить файлы скриптов и стилей, и посжимать.
    Можно сделать это руками, можно взять готовый плагин WP-Minify. Он кстати еще и код страницы вычищает, убирает лишние пробелы между тегами.
  • +1
    Хочу поделиться довольно прогрессивной утилиткой по оптимизации и ускорению WP сайта.
    WEBO Site SpeedUp для WordPress
    Базовые функции — бесплатно, полный фарш — за деньги (можно потестить 14-й триально).
    (Так или иначе, все равно кому-то да приходится платить за скорость).

    На странице данного «решения» есть так же небольшой обзор сравнения конечной оптимизации плагинами для WP.
    • 0
      УГ утилита. Результаты дает хуже других бесплатных плагинов, сайт с ней иногда подглючивает по непонятным причинам, иногда вместо десктопной версии внезапно открывается мобильная… Просидели на нем равно две недели и удалили нафиг.
  • 0
    bigpicture.ru (бигпикче) надо взять на заметку, уж очень тупо у них статика грузится.
    • 0
      уже взяли :)
  • 0
    Тоже мучились с плагином Super Cache, потом установили связку плагинов DB Cache Reloaded и Hyper Cache — все проблемы сами отпали.

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