Лаборатория Кода

Быстрый сайт, или Немного о том, как тяжелые и неповоротливые слоны становятся легкими и ловкими гепардами

Сравнение обычного сайта с тяжеловесным и неповоротливым слоном, в отличие от сравнительно легкого и ловкого гепарда, произошло на основе разработки memcached, — технологии, созданной для высоконагруженных проектов вроде LiveJournal и др. Однако часто владельцы обычных сайтов не имеют выделенного сервера, — установка подобного модуля невозможна, — а быстрый сайт все-таки хочется. Посмотрим, что можно сделать в обычных условиях, на типичном платном хостинге (PHP, MySQL)

Сжатие сайта «на лету» (.htaccess)

Главная страница Yahoo, весящая 101 Kb, загружается как 15-килобайтовая. Как?
GZIP-компрессия, отлаженная на стороне сервера (файл .htaccess), позволяет сжимать файлы «на лету». На стороне браузера происходит «распаковка». В результате, сайт открывается в десятки раз быстрее. Несколько строк, которые нужно добавить в .htaccess файл (должен быть включен mod_deflate или mod_gzip – поинтересуйтесь об этом у хостера):

<IfModule mod_deflate.c>

# сжимаем text, html, javascript, css, xml:
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript

# или, сжимаем файлы по расширению:

<Files *.html>
SetOutputFilter DEFLATE
</Files>

# Исключаем старые версии браузеров, не ладящие с компрессией
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html

Header append Vary User-Agent env=!dont-vary

</IfModule>

Что происходит? Посмотрим поведение браузера и сервера при работе без компрессии:

1. Браузер: Запрашиваю index.html (запрос GET me /index.html)
2. Сервер: Ок, дайте мне взглянуть, есть ли index.html по-близости…
3. Сервер: Нашел его! 100KB. Я посылаю файл. (Код ответа 200)
4. Браузер: Целых 100KB?! Ох… подождите, подождите, идет загрузка… Ок, загружен.

А теперь посмотрим работу браузера и сервера с участием настроек компрессии:

1. Браузер: Эй, могу ли я получить index.html? (запрос GET me /index.html) Или у вас есть сжатая версия?
2. Сервер: Дайте мне найти файл… Да, он здесь. Вы примете сжатую версию?
3. Сервер: Я архивирую найденный index.html (200 OK). Пожалуйста, получите 10 КВ.
4. Браузер: Всего 10КВ? Отлично! Я разархивирую файл и показываю пользователю.

Сжатие сайта «на лету» (php)

Сжатие с помощью PHP осуществляется всего двумя строчками кода в начале и в конце php-скрипта. В начало ключевого скрипта (обычно это index.php для большинства CMS) вносится строка:

<?php
ob_start("ob_gzhandler");

— в конец:

ob_flush();
?>

Проверку, сжимаются ли страницы, можно устроить по адресу: http://www.whatsmyip.org/http_compression/

Форматы для кеширования. Компрессия изображений, flash, видео и музыкальных форматов — не дает никакого эффекта и, по факту, бессмысленна. Обычно для компрессии выделяют так называемую «Большую Тройку»: HTML, Javascript, CSS.
Нагрузка на сервер: Процесс разархивации происходит почти мгновенно, т.к. затрачивает резервы на стороне компьютера пользователя, а не сайта. При архивации (компрессии) на стороне сервера, происходит некоторый расход CPU-ресурсов (нагрузка на сервер), однако, при этом, уменьшается трафик.

Кеширование

Кеширование способно многократно уменьшить нагрузку на сервер и увеличить скорость загрузки страниц сайта. Для кеширования добавляем эти строки в .htaccess файл:

#кешируем html, htm на 12 часов
<FilesMatch ".(html|htm)$">
Header set Cache-Control "max-age=43200"
</FilesMatch>

#кешируем css, javascript и txt-файлов на неделю
<FilesMatch ".(js|css|txt)$">
Header set Cache-Control "max-age=604800"
</FilesMatch>

# кешируем flash и картинок на месяц
<FilesMatch ".(flv|swf|ico|gif|jpg|jpeg|png)$">
Header set Cache-Control "max-age=2592000"
</FilesMatch>

#запрет на кеширование скриптов
<FilesMatch "\.(pl|php|cgi|spl|scgi|fcgi)$">
Header unset Cache-Control
</FilesMatch>

Роботы и нагрузка на сервер

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

Добавляем следующую запись в файл «robots.txt»:

User-agent: *
Crawl-delay: 6

Параметр «Crawl-delay» отвечает за время (в секундах), в которое поисковик будет обращаться за следующей страницей сайта. Для большего эффекта, можно увеличить время с 6-ти до 12-ти секунд. Желательно оградить сайт от излишнего сканирования, запретив определенные каталоги и области сайта, например:

User-agent: *
Disallow: /admin/
Disallow: /search

Чтобы оградить сайт от нежелательных роботов, можно прописать в файле .htaccess следующие строки:

SetEnvIfNoCase user-Agent ^FrontPage [NC,OR]
SetEnvIfNoCase user-Agent ^Java.* [NC,OR]
SetEnvIfNoCase user-Agent ^Microsoft.URL [NC,OR]
SetEnvIfNoCase user-Agent ^MSFrontPage [NC,OR]
SetEnvIfNoCase user-Agent ^Offline.Explorer [NC,OR]
SetEnvIfNoCase user-Agent ^[Ww]eb[Bb]andit [NC,OR]
SetEnvIfNoCase user-Agent ^Zeus [NC]
<limit get="" post="" head="">
Order Allow,Deny
Allow from all
Deny from env=bad_bot
</limit>

Список User Agent браузеров, роботов и пауков поисковых машин, веб-каталогов, менеджеров закачек, спам-ботов и плохих ботов можно найти на сайте List of User-Agents.

Оптимизация баз данных и php-запросов

Для уменьшения нагрузки на сервер часто бывает необходимым оптимизировать и/или минизировать php-запросы к базам данных.

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

Большинство CMS используют динамичные скрипты обработки, а так же шаблоны / темы (статичный контент, использующий язык обращения к динамичной обработке). Столкнувшись с проблемой нагрузки на сервер, многие находят выход в том, чтобы ограничить запросы из шаблона, связанные с базами данных. Это могут быть линии в определении кодировки страниц; линии, связанные с версией установленной CMS и др.

Hand-made касается переписания вручную динамичных блоков, часто запрашиваемых на сайте (главное меню, которое редко меняется и, тем не менее, каждый раз обращается к БД; подвал («Footer») и т.д.)

Этот Hand-made, в самом деле, способен сократить нагрузку на сервер и ускорить загрузку страниц.

Излишние запросы к базе данных приводят к нагрузке сервера, а иногда и «засыпанию»  (bd_name sleep) баз данных (медленные запросы). Медленные запросы могут встречаться не в регулярных блоках, описанных выше, а в динамичных – например, при постраничном выводе материалов, поиске по сайту.

Есть несколько общих принципов по оптимизации запросов к базе MySQL.

1.    Конкретизируйте запрос: вместо «SELECT * from site_users» используйте конкретные поля, которые вам нужны в контексте страницы, например: «SELECT UserID, UserName, FirstName, LastName from site_users». Это актуально, если таблица пользователей (site_users) насчитывает множество полей индивидуальных настроек пользователя. Используйте «SELECT * from site_users» только в случае, если запрос касается одного пользователя, — например, на странице его профиля, — и вам нужно большинство из имеющихся полей (нет смысла перечислять все).
2.    Используйте «COUNT» для подсчета. При этом, лучше вести подсчет по ID, чем по всем полям (*): «SELECT COUNT(UserID) from site_users». Вместе с тем, запрос «SELECT COUNT(*)» считается достаточно быстрым при использовании без дополнительных условий (WHERE).
3.    Используйте лимит выдачи, если вам нужно ограниченное число для вывода: «SELECT UserID, UserName, FirstName, LastName
from site_users
limit 10».
4.    Создание индексов. Если вы используете запросы вида «SELECT UserID, UserName, FirstName, LastName
from site_users
where tab1 = 1 and tab2 = 1
limit 10», нужно проставить индексы соответственно полей tab1 и tab2.
Однако нужно помнить, что излишние индексы на ненужных полях тоже вредны.
Проставление индексов актуально, если вы расширяете функционал CMS и добавляете в таблицах новые поля, по которым проводите выборку.
5.    Избегайте усложнения запроса. Не гонитесь за двумя зайцами сразу. Лучше иметь два легких и быстрых запросов, чем один сложный и тормозной. Например, не стоит пытаться в одном запросе выбирать максимальное количество поинтов (MAX) у пользователя из таблицы site_users и количество записей (COUNT) из таблицы users_notes. Такой запрос лучше разделить на два, где в одном будет выбираться MAX, а в другом подсчитываться количество записей.
6.    Запросы SELECT, INSERT («выбрать», «вставить») являются наиболее быстрыми по сравнению с UPDATE, DELETE («обновить», «удалить»). Учитывайте это при написании новых скриптов. Также, стоит обратить внимание на использование некоторых «тормозных» ф-ций внутри запроса (например, DISTINCT, RAND и др.).
7.    Используйте «GROUP BY» для групповых функций (MIN(), MAX() и др.). Вызов групповых функций для SQL-команд, не содержащих «GROUP BY», эквивалентен выполнению этих функций над всем набором возвращаемых данных. Пример запроса:  «SELECT student_name, MIN(test_score), MAX(test_score)
FROM student
GROUP BY student_name;»

Для проверки быстродействия или сравнения двух запросов, используйте оператор EXPLAIN.

Синтаксис:

EXPLAIN имя_таблицы
или EXPLAIN SELECT опции_выборки

Этот материал опубликован в Лаборатория Кода и тегирован , , , , , , , , . Bookmark the permalink.

Один отзыв на Быстрый сайт, или Немного о том, как тяжелые и неповоротливые слоны становятся легкими и ловкими гепардами

  1. hotshot bald cop говорит:

    Why is it I all the time really feel like you do?

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

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

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>