# теория · шаг 2 из 4
Как измерить эффект и не обмануться
Коротко
Любую настройку из прошлого урока легко включить и невозможно оценить на глаз. Этот урок - про то, как не поверить в улучшение, которого не было.
- Сначала раздели:
$request_timeбольшой при маленьком$upstream_response_time- тормозит отдача; оба большие - тормозит приложение. - Смотри процентили, а не среднее: среднее прячет хвост.
- Мерить надо ту сторону, где идут данные, и повторять прогоны: на трёх прогонах картина может выйти обратной.
- Состояние кеша между прогонами обязано быть одинаковым, иначе сравниваешь не то, что думаешь.
Если умеешь читать процентили и не путаешь $request_time с $upstream_response_time - листай до «Теперь сам».
Сначала ответь сам
Включили sendfile, прогнали нагрузочный тест - стало на 15% быстрее. Можно
записывать в достижения?
Пока нет. Между двумя прогонами изменилось не только sendfile: сменилось
состояние кеша страниц, кеша бэкенда и открытых соединений. Классическая ошибка
- перезапустить nginx, прогнать тест и записать разницу на счёт правки.
Правильный вывод из такого замера один: надо прогнать ещё раз. И желательно не два, а больше - об этом ниже, с числами.
На что это похоже
Взвешиваться после спортзала. Весы покажут минус килограмм, и соблазн записать это на счёт тренировки велик - хотя ушла вода, а через час она вернётся.
Честный замер требует одинаковых условий до и после: те же весы, то же время суток, то же состояние. У сервера «состояние» - это прогретые кеши и установившиеся соединения.
Механизм: три числа отвечают на вопрос раньше догадок
log_format perf '$status $body_bytes_sent $request_time $upstream_response_time '
'$upstream_cache_status "$request"';
Считать «в среднем» бесполезно: среднее прячет хвост. Смотри 95-й и 99-й процентили - именно они описывают то, что чувствует живой человек.
Правило
ab -n 2000 -c 50 https://shop.local/assets/app.4f2c1b.js
ab (ApacheBench) - утилита нагрузки: -n 2000 всего запросов, -c 50
одновременно. Ниже - две строки из её отчёта (полный длиннее: он печатает
таблицу 50/66/75/80/90/95/98/99/100%):
Requests per second: 4821.55 [#/sec] (mean)
95% 14
99% 28
Процентиль - это значение, ниже которого укладывается столько-то процентов запросов: «95% 14» значит, что 95 из 100 ответов пришли быстрее 14 мс. Четыре правила, без которых замер врёт:
- Один параметр за раз. Включили
sendfile- измерили. Потомtcp_nopush- измерили. Иначе непонятно, что дало эффект. - Греть перед замером. Первые запросы читают файл с диска и наполняют кеш; сравнивать надо установившийся режим.
- Мерить не с той же машины, если проверяешь сеть: у петли другая физика.
- Смотреть на процессор сервера, а не только на цифры клиента.
topво время прогона показывает, упёрся ты в nginx или в канал.
Разбор: три прогона могут соврать
Замер из десятого раздела - сравнение HTTP/2 и HTTP/3 на сети с потерями. По трём прогонам, взяв лучшее время, HTTP/3 выглядел победителем: 1513 мс против 4461. Повторили - и HTTP/3 показал 5736 против 5220, то есть проиграл.
Семь прогонов расставили всё по местам:
HTTP/1.1 (шесть соединений) 1017 1120 1214 1237 1341 1420 1767
HTTP/2 (одно соединение) 1313 1413 1714 2021 5221 5268 5486
HTTP/3 (одно соединение) 2744 6250 6464 7532 7807 7934 9306
x6 в первой строке значило «шесть параллельных соединений HTTP/1.1» - развёрнуто
ради ясности. Разброс у HTTP/2 - четырёхкратный, у HTTP/3 - трёхкратный. На такой выборке
«лучшее из трёх» - это не измерение, а лотерея. Правило: чем больше разброс,
тем больше нужно прогонов, и смотреть надо на медиану вместе с хвостом, а не
на одно число.
Разбор: мерить надо ту сторону, где данные
Тот же замер сначала не показывал вообще ничего: HTTP/2 давал 813 мс и при нуле потерь, и при пяти процентах. Потери стояли на клиенте - то есть на стороне, где идут короткие запросы, а не мегабайты ответов.
Как только потери переехали на сторону сервера, разница проявилась немедленно: HTTP/2 вырос с 814 до 2021 мс по медиане.
Обобщение простое: инструмент надо ставить туда, где течёт трафик. Меряешь отдачу - ограничивай канал на отдающей стороне. Меряешь загрузку файлов - на клиентской.
Что ломается без этого
Третья ловушка того же рода - состояние кеша. При проверке proxy_cache_lock
десять одновременных запросов дали десять обращений к бэкенду и с блокировкой, и
без неё: в каталоге кеша лежали файлы от прошлого прогона, а зона в памяти после
перезапуска была пуста, и nginx перечитывал всё заново.
Стоило вычистить каталог перед прогоном - и картина стала однозначной:
без proxy_cache_lock: 10 обращений к бэкенду
с proxy_cache_lock: 1 обращение (1 MISS + 9 HIT)
Полезный побочный факт: сам по себе кеш перезапуск переживает - файлы на
диске никуда не деваются, и прогретая запись остаётся HIT после restart. Это
отличает его от счётчиков limit_req, которые обнуляются.
Зачем это в работе
Порядок приёмов по отдаче, от большого эффекта к малому:
| приём | что улучшает |
|---|---|
| кеш ответов бэкенда | $upstream_response_time уходит из картины целиком |
| сжатие | размер ответа, а значит время на медленных каналах |
| кеш-заголовки (пятый раздел) | повторные запросы исчезают вовсе |
sendfile плюс tcp_nopush |
процессорное время на раздаче |
aio/directio |
только на файлах больше памяти |
worker_processes, worker_connections |
ничего, пока ты в них не упёрся |
Последняя строка - самая частая ошибка. Воркеры и лимиты соединений крутят
первым делом, хотя они не ускоряют ни один запрос: они задают потолок, после
которого сервер начинает отказывать. Нет в логе worker_connections are not
enough - не трогай.
Теперь сам
Пользователи жалуются на медленный сайт. В логе $request_time в 99-м
процентиле - 4 секунды, $upstream_response_time - 3,9 секунды. Что чинить и
что точно не поможет?
Чинить приложение или базу: почти всё время уходит на бэкенд. Не помогут ни
sendfile, ни tcp_nopush, ни воркеры - они про отдачу, а отдача занимает
десятую долю секунды. Первый ход - кеш ответов из следующей главы: он убирает
$upstream_response_time из картины для той части запросов, которые можно
кешировать. Проверить гипотезу можно за тридцать секунд, сравнив TTFB (time to
first byte, время до первого байта - curl -w '%{time_starttransfer}') через
nginx и напрямую к приложению.
Главное
Сначала измерь, что именно медленно: $request_time против
$upstream_response_time разделяет «медленно отдаём» и «медленно считает
приложение», а смотреть надо процентили, а не среднее. Замер честен только при
одном изменении за раз, прогретом и одинаковом состоянии кешей и достаточном
числе прогонов - на трёх прогонах разброс легко переворачивает вывод. Ограничение
канала ставят на ту сторону, где идут данные. Наибольший эффект дают кеш, сжатие
и кеш-заголовки; воркеры не ускоряют ничего, пока в них не упёрся.
Комментарии
Пока нет комментариев. Будь первым!
Оставить комментарий