Как программист всегда интересовался методами оптимизации кода, и при разработке портала Govzalla.com испробовал кое-какие подходы оптимизации как серверной части, так и клиентской. Надеюсь, мои эксперименты, о которых расскажу ниже, помогут другим нашим веб-разработчикам т.к. многие сайты в Ченете находятся, в этом плане, в ужасном состоянии. В этой части речь пойдет только о серверной части, в следующей части, Даламукълахь, расскажу об оптимизации клиентской части.

В начале был LAMP

Кажется, это был конец 2014, когда запустил портал. Работал он на чистом WordPress + использовался шаблон, название которого я уже не помню. На сервере была привычная сборка: Debian (Wheezy), Apache2, MySQL и PHP 5.6. Казалось бы, все норм, все как у всех. Но со временем начала надоедать медленная загрузка страниц, time to first byte (TTFB) превышал (или превышала?) 1 секунду, хотя в то время не особо обращал внимания именно на этот фактор.

Пришлось искать возможности ускорения загрузки сайта, и конечно первое, что мне Google выдал были ссылки на разные WP кэш плагины. Одним из таких был популярный W3 Total Cache, который некоторое время нам и служил.

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

Со временем я понял, что было бы неплохо оптимизировать и сервер, и первое что я сделал - заменил корову Apache2 на более быстрый Nginx. И вуаля, страницы начали загружаться в 2 раза быстрее. Должен сказать, это одно из тех решений, о которых я никода не пожалел. Пока есть Nginx не вижу ни одной причины переходить на Apache2.

HHVM - чудо от Facebook

На смене веб-сервера решил не останавливаться. Читая разные статьи в Интернете наткнулся на рекомендации использовать HHVM вместо PHP, чего я и сделал. В результате удалось еще в разы ускорить загрузку страниц. Если не ошибаюсь, именно тогда удалось достич TTFB ~150ms. И это на WordPress, который в народе считается медленным.

Благодаря трансляции PHP код в C++, HHVM по сравнению с PHP5.6 оказался действительно супер быстрым.

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

PHP7 vs HHVM

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

[AdSense-A]

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

MySQL vs MariaDB

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

После долгих поисков нашел MariaDB - форк MySQL, сделанный после перехода MySQL под Oracle. MariaDB разрабатывается разрабочтиками, которые прежде работали над MySQL. По их словам MariaDB должен превосходить  MySQL в скорости работы, но должен признаться, я никакой разницы не заметил. Так что какое-то время MariaDB стоял на сервере, но позже вернулся к MySQL и настроил необходимые настройки кэширования запросов. Кстати MariaDB полностью совместим с MySQL, включая консольных команд.

Оптимизация запросов

Несмотря на отсутствие более быстрой замены MySQL, некоторые запросы все таки необходимо было оптимизировать. У нас самыми проблемными являются запросы для расчета репутации пользователя.

2 * PS + (2 * PL - 1 * PD) + 2 * FT + 0.2 * FR + 0.2 * BP BS + 0.5 * RL + 0.5 * CL
  • PS (post standard) - количество добавленных статей
  • PL (post likes) - количество полученных лайков за статьи
  • PD (post dislike) - количество полученных дислайков за статьи
  • FT (forum topics) - количество созданных тем на форуме
  • FR (forum replies) - количество добавленных ответов на форуме
  • BS (blog subscribers) - количество подписчиков в блоге
  • BP (blog posts) - количество постов в блоге
  • RL (forum reply like) - количество полученный лайков за ответы на форуме
  • CL (comment like) - количество полученных лайков за комментарии

Так, для того, чтобы расчитать репутацию одного пользователя делается SQL запрос SELECT, содержащий в себе другие ~10 SELECT запросов - казалось бы, ничего страшного, но если открыть какую-нибудь тему на форуме, то она может содержать до 20 постов, где у каждого поста указан автор с его репутацией, а это значит, что совершаются около ~200 SELECT запросов прежде, сервер вернеть страницу темы. Естественно, такую проблему нельзя было оставить без внимания и для решения данной проблемы был использован сервер memcached.

Memcached - сервер, хранящий в операционной памяти ключ-значение. Конкретно у нас он хранит эти данные до 1 часа, в результате запросы для расчета репутации совершаются лишь раз за час.

Если раньше функция, которая возвращает репутацию выглядела так:

То сейчас она выглядит примерно так:

Ubuntu 16.04 и HTTP2

Если в начале стоял Debian Wheezy, то со временем было решено перейти на Ubuntu 16.04. Причина: протокол HTTP2, который не поддерживается устаревшими библиотеками Debian Wheezy, а компилировать новые библитеки в устаревшей системе мне не хотелось.

[AdSense-B]

Почему именно Ubuntu 16.04?

Потому, что для меня если Linux, то только Debian based, да и на тот момент у меня на локальной системе был установлен именно 16.04.

Почему HTTP2?

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

https://vimeo.com/148855633

Заключение

После всех выше описанных экспериментов для меня оптимальным оказался следующий вариант:

  • Ubuntu 16.04
  • Nginx
  • PHP7 + OpCache
  • MySQL + Memcached
  • HTTP2

[caption id="attachment_150986" align="aligncenter" width="985"] Результат тестирования через https://tools.pingdom.com[/caption]

Напишите в комментах какие минусы видите в этом варианте, и какие методы оптимизации знаете вы.