Optimization
Материал из OpenWiki
Заметки по оптимизации на web-сайтах
- Все что можно - преобразовывать в статику. Предгенерация контента.
- Когда предгенерация не подходит - кэшируем динамику "на лету". Первый уровень - самые популярные и часто обновляемые блоки кешируем в памяти (memcached). Второй уровень - редко запрашиваемые блоки кэшируем на диск. Кэшируем на уровне приложений. Чистим запись в кэше в момент изменения данных связанных с прокэшированными данными, если от изменяемого элемента зависит несколько блоков кэша - определяем ключевую зависимость (элемент - список блоков кеша для очистки). Если операция восстановление элемента кэша русурсоемкая или высокая интенсивность запросов, т.е. может возникнуть ситуация когда два процесса одновременно начнут перестроение одного блока кэша, необходимо не чистить кэш, а использовать атомарное флаговое поле, сигнализируюшее о необходимости обновления блока кеша. Первый процесс заметивший признак обновления, меняет убирает этот признак перестроения и после формирования контента атомарно переписывает содержимое блока кэша, остальные процессы до завершения генерации нового сорежимого кэша продолжают использовать старое значение кэша. Если есть вероятность сбоя генерирующего контент процесса, можно использовать дополнительные таймауты для ограничения времени жизни кэша, которые в обычном режиме достаточно велики, но при начале перегенарации элемента кэша уменьшаются до минимально допустимых значений, а после перезаписи блока кэша опять увеличиваются до значительных величин.
- Буферизируем вывод apache через nginx. На уровне nginx реализуем базовые "url rewrite" правила, сжатие контента, перекодировку, средства борьбы с флудом, ограничения числа одновременных потоков. Возможна организация целостного кэширвоания страниц, через связку nginx+memcached с предварительным формированием кэша на уровне приложений.
- Периодически парсим лог и выявляем пики дублирующих друг друга запросов к динамике (например, флуд через один и тот же сложный запрос к поисковой подсистеме), автоматически кэшируем результат таких запросов в статику и, пока не прекратится флуд, перенаправляем в статику. Также выявляем аномально больше число запросов динамики с одного IP, если при этом превышен допустимый Load Average, временно блокируем IP.
- Со временем убедился, что персональные настройки на сайте - лишнее усложнение, почти всегда достаточно разумного дефолта. Стремлюсь к простоте в оформлении и лаконизму в изложении.
- В динамике - поиск (возможно кэширование индексов популярных ключей через memcached), динамическая часть форума (возможно кэширование последних БД файлов, сессий и индексов в memcached. Перевод списочных страниц в статику, путем выставления new и флагов привязанных к пользователю через JavaScript), контент который не выгодно хранить или отдавать в статике:
- когда контент слишком часто изменяется;
- когда необходимо обеспечить большую гибкость запроса (можно кэшировать наиболее типичные запросы);
- когда объем контента в статике слишком велик (можно выделить самое популярное и кэшировать его);
- когда запрашивается только "верхушка айсберга" (например - вывод текста новостей, интенсивно запрашиваются только последние новости, которые и можно предкэшировать "на лету", но не предгенерировать);
- когда предгенерация требует значительных ресурсов, несопоставимых с ресурсами затрачиваемыми при отдаче того-же динамикой (например, когда много вариантов представления - выгоднее предкэшировать отдельные блоки, а не всю страницу сразу).
- Все что нельзя превратить в статику (предгенерировать) - кэшировать через статику. Предкэширование типовых запросов и промежуточное сохранение часто используемых выборок в кэше. Например, можно прокэшировать только наиболее часто встречающиеся запросы.
- Ту динамику, что невозможно прокэшировать - снабжать средствами защиты от перегрузок.
- защитой от DoS/flood потоков запросов;
- ограничить число одновременных потоков на клиента;
- ограничить число запросов на клиента в ед. времени (средства ipfw, mod_limitconn или mod_throttle);
- при превышении разумных значений Load Average можно перейти в режим ограниченной функциональности, например, перестать принимать POST запросы от клиентов без Referrer или без cookie;
- квоты на дисковое пространство. Не переусердствовать, отделить первичные области диска (например, хранилище БД, данные форума - не восстановимы), от вторичных (например, предгенирированный контент, кэши - влияют на отображение, но восстановимы из первичных областей) и третичных (например, логи - потеря не так страшна);
- квоты на процессорное время (например, можно ограничить время выполнения скрипта);
- квоты на размер процесса;
- квоты на число процессов;
- Практические рекомендации по тюнингу Apache:
- FreeBSD ядро собираем с "options ACCEPT_FILTER_HTTP" (или "kldload accf_http"), в конфигурацию Apache добавляем "AcceptFilter on", перезапускаем Apache;
- Убираем лишние модули;
- "KeepAlive Off", а картинки отдаем через отдельный мини-http сервер для отдачи статики (mathopd, thttpd, Nginx, lighttpd);
- В "Options" должно быть "FollowSymLinks", но не нужно "SymLinksIfOwnerMatch" (приводит к лишней lstat() проверке при каждой отдаче файла).
- Не используем .htaccess (AllowOverride None), все настройки, включая авторизацию, в httpd.conf (apache тратит ресурсы на попытки открыть .htaccess в каждой родительской директории);
- Ограничиваем число одновременных запросов с одного IP через "ipfw add allow tcp from any to $IP 80 via fxp0 setup limit src-addr 15" или "iptables -A INPUT-p tcp --dport 80 -m iplimit --iplimit-above 10 -j REJECT", но не через модули apache (mod_throttle, mod_bandwidth, mod_limitconn);
- Использовать FastCGI/mod_perl для динамики (устраивает использование FastCGI). Наиболее выгодно использовать для небольших скриптов, время загрузки и парсинга которых сравнимо с временем выполнения скрипта, либо если промежуточные данные можно держать в памяти (пример, скрипт "хинтов" в opennet). Выгода почти не ощутима для тяжелых скриптов, требующих значительного времени для выполнения и не использующих одни и те же области данных (например - скрипты поиска).
- Использовать mod_accel - для буферизации клиентских запросов и создания внешнего кэша запросов к динамике. Увеличение sendbuffer в apache и send/recv буферов на уровне ОС. Написание скриптов с учетом кэширования. (в opennet mod_accel не используется)
- Использовать mod_deflate для сжатия отдаваемого пользователям контента (трафика меньше и клиенту быстрее информация отдается, нагрузка на CPU от сжатия - почти незаметна);
- Если статики очень много (например отдаются картинки) - не тратим на нее силы apache, а отдаем через nginx. Окажет помощь балансировка данных на несколько дисков, особенно если на сервере используются SCSI накопители. Или выдача статики прямо из memcached или ram-диска.
Дополнительные системы увеличения производительности
- Кэширование контента.
- Кэширование SQL запросов.
- Кэширование промежуточных данных
- Кэширование результата.
- Предкешированные объекты.
- Использование оптимальных алгоритмов.
- Оборудование - SCSI диски, машины с SMP.
- Тюнинг ОС.
- Разделение нагрузки на несколько серверов, вынос сервисов на отдельные машины.
- Функциональное разделение (по IP) и равномерное разделение (SQL)
- Субъективное разделение (разделение функций и операций, DNS-балансирование, IP-балансирование и т.д.)
- Разделение процессов (кластер, 2 SQL сервера для балансировки SELECT запросов.)