Сравнение обычного сайта с тяжеловёсым и неповоротливым слоном в противовес лёгкому и ловкому гепарду возникло на основе разработки memcached — технологии, созданной для высоконагруженных проектов вроде LiveJournal и других. Однако владельцы обычных сайтов часто не имеют выделенного сервера, а установка подобного модуля невозможна, хотя быстрый сайт всё-таки хочется. Посмотрим, что можно сделать в обычных условиях на типичном платном хостинге (PHP, MySQL)
Сжатие сайта «на лету» (.htaccess)
Главная страница Yahoo, которая весит 101 КБ, загружается как 15-килобайтовая. Как?
GZIP-компрессия, настроенная на стороне сервера (файл .htaccess), позволяет сжимать файлы «на лету». На стороне браузера происходит их распаковка. В результате сайт открывается в разы быстрее. Несколько строк, которые нужно добавить в файл .htaccess (должны быть включены mod_deflate или mod_gzip — уточните это у хостера):
# сжимаем 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
Что происходит? Посмотрим на поведение браузера и сервера при работе без компрессии:
- Браузер: Запрашиваю index.html (запрос
GET /index.html). - Сервер: Хорошо, дайте мне взглянуть, есть ли index.html рядом…
- Сервер: Нашёл! 100 КБ. Посылаю файл. (Код ответа 200).
- Браузер: Целых 100 КБ?! Ох… подождите, подождите, идет загрузка… OK, загружен.
А теперь посмотрим на работу браузера и сервера с настройками компрессии:
- Браузер: Эй, могу я получить index.html? (запрос
GET /index.html). Или у вас есть сжатая версия? - Сервер: Дайте мне найти файл… Да, он здесь. Вы примете сжатую версию?
- Сервер: Я архивирую найденный index.html (200 OK). Пожалуйста, получите 10 КБ.
- Браузер: Всего 10 КБ? Отлично! Я разархивирую файл и покажу пользователю.
Сжатие сайта «на лету» (PHP)
Сжатие с помощью PHP осуществляется всего двумя строчками кода в начале и в конце php-скрипта. В начало ключевого скрипта (обычно это index.php для большинства CMS) вносится строка:
<?php
ob_start('ob_gzhandler');
В конец:
<?php
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 "\.(css|js|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>
# запрет на кеширование скриптов (пример для php)
<FilesMatch "\.(php)$">
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]
Order Allow,Deny
Allow from all
Deny from env=bad_bot
Список User-Agent браузеров, роботов, пауков поисковых систем, веб-каталогов, менеджеров закачек, спам-ботов и вредоносных программ можно найти на специализированных сайтах, например, List of User-Agents.
Оптимизация баз данных и PHP-запросов
Для уменьшения нагрузки на сервер часто бывает необходимо оптимизировать и/или минимизировать PHP-запросы к базам данных.
Первое, что нужно сделать для облегчения нагрузки, — это просмотреть запросы, выполняемые в тех или иных (или во всех) разделах сайта, и избавиться от тех, результат которых можно заменить статичным контентом.
Большинство CMS используют динамичные скрипты обработки, а также шаблоны/темы (статичный контент, который обращается к динамичным обработчикам). Столкнувшись с проблемой нагрузки на сервер, многие находят выход в ограничении запросов из шаблона, связанных с базами данных. Это могут быть строки, определяющие кодировку страниц, или строки, связанные с версией установленной CMS и другие.
Ручная оптимизация (Hand-made) заключается в переписывании динамичных блоков, которые часто запрашиваются на сайте (например, главное меню, которое меняется редко, но каждый раз обращается к БД; «подвал» (Footer) и т.д.). Такой подход действительно способен сократить нагрузку на сервер и ускорить загрузку страниц.
Излишние запросы к базе данных приводят к нагрузке на сервер, а иногда и к «засыпанию» (bd_name sleep) баз данных из-за медленных запросов. Медленные запросы могут встречаться не только в регулярных блоках, но и в динамичных — например, при постраничном выводе материалов или поиске по сайту.
Есть несколько общих принципов по оптимизации запросов к базе MySQL.
- Конкретизируйте запрос. Вместо
SELECT * from site_usersиспользуйте конкретные поля, которые нужны в контексте страницы, например:SELECT UserID, UserName, FirstName, LastName from site_users. Это особенно актуально, если таблица пользователей (site_users) насчитывает множество полей. ИспользуйтеSELECT * from site_usersтолько в случае, если запрос касается одного пользователя (например, на странице его профиля) и вам нужно большинство из имеющихся полей (нет смысла перечислять все). - Используйте COUNT для подсчёта. При этом лучше вести подсчёт по ID, а не по всем полям (
*):SELECT COUNT(UserID) from site_users. Вместе с тем запросSELECT COUNT(*)считается достаточно быстрым при использовании без дополнительных условий (WHERE). - Используйте лимит выдачи, если нужно ограниченное количество записей:
sql SELECT UserID, UserName, FirstName, LastName FROM site_users LIMIT 10 - Создавайте индексы. Если вы используете запросы вида:
sql SELECT UserID, UserName, FirstName, LastName FROM site_users WHERE tab1 = 1 AND tab2 = 1 LIMIT 10
нужно проставить индексы для полейtab1иtab2.
Однако помните, что излишние индексы на редко используемых полях тоже вредны. Проставление индексов актуально, если вы расширяете функционал CMS и добавляете в таблицы новые поля, по которым проводите выборку. - Избегайте усложнения запроса. Не гонитесь за двумя зайцами сразу. Лучше иметь два лёгких и быстрых запроса, чем один сложный и медленный. Например, не стоит пытаться в одном запросе выбирать максимальное количество очков (
MAX) у пользователя из таблицыsite_usersи количество записей (COUNT) из таблицыusers_notes. Такой запрос лучше разделить на два. - Запросы
SELECT,INSERT(«выбрать», «вставить») являются наиболее быстрыми по сравнению сUPDATE,DELETE(«обновить», «удалить»). Учитывайте это при написании новых скриптов. Также стоит обратить внимание на использование некоторых «медленных» функций внутри запроса (например,DISTINCT,RANDи др.). - Используйте
GROUP BYдля групповых функций (MIN(),MAX()и др.). Вызов групповых функций для SQL-команд, не содержащихGROUP BY, эквивалентен выполнению этих функций над всем набором возвращаемых данных.
Пример запроса:sql SELECT student_name, MIN(test_score), MAX(test_score) FROM student GROUP BY student_name;
Для проверки быстродействия или сравнения двух запросов используйте оператор EXPLAIN.
Синтаксис:
EXPLAIN имя_таблицы;
или
EXPLAIN SELECT опции_выборки;