PostMedium ⏱ ~4 min

Нагрузочное тестирование: как читать графики и не паниковать

Table of contents

В предыдущих частях я писал про установку k6, сценарии и визуализацию. Теперь — про самое сложное: как читать графики и отличать реальную проблему от шума.

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

Здесь я расскажу, как я сейчас анализирую результаты. Без математической воды, только то, что помогает принять решение.

Шаг 1: Проверяю, что тест прошёл корректно

Перед тем как анализировать цифры, я смотрю на базовые вещи:

  • Дошло ли нужное количество VU до конца прогона?
  • Не упал ли сам генератор нагрузки по CPU или памяти?
  • Совпадает ли количество запросов с ожидаемым?

Если k6 упал с out of memory, или VU не добрались до target, или запросов в 3 раза меньше, чем должно быть — анализировать метрики бессмысленно. Надо чинить инфраструктуру или скрипт.

Шаг 2: Смотрю на throughput (RPS)

Первый график, который я открываю — RPS (requests per second). Он показывает, сколько запросов в секунду приложение реально обрабатывает.

Что я ищу:

  • Плато — приложение вышло на стабильную пропускную способность.
  • Падение RPS при росте VU — значит, где-то bottleneck. Возможно, сервер начал отбрасывать запросы, или БД зависла.
  • Скачки вниз — возможно, GC-паузы, перезапуски контейнеров, или срабатывание rate limiting.

Пример: я видел график, где RPS рос линейно до 400, потом резко упал до 200. Оказалось, что пул соединений с БД закончился, и новые запросы стали ждать.

Шаг 3: Задержки (response time)

Среднее время ответа — почти бесполезная метрика. Она скрывает выбросы. Я смотрю на процентили. Напомню из первой части: процентиль — это точка распределения, а не отдельная метрика. «p95 = 400 мс» значит, что 95% запросов уложились в 400 мс или быстрее. И считать их можно по разным метрикам: полная длительность запроса, время до первого байта, время получения ответа — в k6 это http_req_duration, http_req_waiting, http_req_receiving соответственно. Дальше везде я говорю про процентили полной длительности запроса:

  • p50 (медиана) — половина запросов быстрее этого значения, половина медленнее. Ближайший аналог «типичного» времени ответа, но устойчивый к выбросам.
  • p95 — «худший нормальный» случай: 95% запросов быстрее этой отметки. Обычно именно его берут в SLA.
  • p99 — хвост распределения: самый медленный процент. Полезен, чтобы ловить выбросы, но при малом объёме запросов скачет из-за статистики.

Если p50 стабилен, а p95 растёт — значит, появляется «хвост» медленных запросов. Обычно это означает, что какой-то ресурс заканчивается: соединения, потоки, память.

Если растут и p50, и p95 — приложение реально тормозит под нагрузкой.

Шаг 4: Ошибки и статус-коды

График статус-кодов часто говорит больше, чем latency. Я смотрю:

КодыО чём говорятЧто делаю
500+Приложение падаетСмотрю логи, ищу первый по времени кейс
502 / 503 / 504Проблема на уровне балансировщика или прокси, либо приложение не успевает отвечатьПроверяю health-checks, таймауты прокси и очередь перед приложением
429Сработал rate limiterПересматриваю настройки теста или прошу исключение для генератора
400Скрипт шлёт некорректные данныеПроверяю payload и тестовые данные

Важное правило: не считайте тест успешным, если есть хоть один 500-й код. Даже 0.1% ошибок при миллионах запросов — это тысячи реальных пользователей.

Шаг 5: Коррелирую с метриками сервера

Графики k6 — это вид со стороны клиента. Чтобы понять причину, я смотрю на метрики сервера в тот же интервал:

  • CPU — если 100%, значит, не хватает вычислительной мощности.
  • Память — если растёт и не падает, возможна утечка.
  • Соединения с БД — если все заняты, запросы ждут.
  • Диск I/O — иногда bottleneck вовсе не в CPU, а в медленных запросах к БД.

Пример из практики: latency росла, CPU был 40%. Я сначала думал, что проблема в коде. Потом посмотрел на БД — active connections забиты, slow queries пошли. Оптимизировал один индекс, и p95 упала в 4 раза.

Шаг 6: Сравниваю прогоны между собой

Один прогон — это точка. Чтобы увидеть тренд, я сравниваю несколько прогонов:

  • До оптимизации и после.
  • С разным количеством VU.
  • С разными сценариями.

Я сохраняю результаты k6 в JSON или отправляю в InfluxDB с тегом версии. Так можно наложить графики друг на друга и увидеть, помогло ли изменение.

Частые ложные тревоги

Не всё, что растёт на графике, — проблема:

  • Warm-up — первые 30-60 секунд обычно медленнее. Не принимайте их за реальный результат.
  • Одиночные пики — могут быть связаны с GC, деплоем соседнего сервиса, или фоновой задачей. Смотрите на тренд, а не на отдельные точки.
  • p99 скачет при малом трафике — если RPS низкий, p99 может прыгать просто из-за статистики. Доверяйте ему только при достаточном объёме данных.

Мой минимальный чек-лист

Когда открываю дашборд после прогона, я иду по этому списку:

  1. VU дошли до target и держались до конца?
  2. RPS стабилен или растёт плавно?
  3. p50 и p95 в пределах SLA?
  4. Есть ли ошибки? Какие?
  5. CPU/память/БД на стенде в норме?
  6. Есть ли корреляция между ростом latency и метриками сервера?
  7. Результат воспроизводится при повторном прогоне?

Что дальше

Нагрузочное тестирование — не разовая акция. Я запускаю его перед релизами, после крупных рефакторингов, когда меняю инфраструктуру. Иногда — просто чтобы убедиться, что цифры не ухудшились.

Главное, что я вынес за эти прогоны: метрики бессмысленны без контекста. Всегда начинайте с вопроса «что мы проверяем?», а не с графиков. Графики — инструмент ответа, а не замена вопросу.

Если остались вопросы — пишите в комментариях, разберу конкретные случаи.

Previous

Discussion

No comments yet - start the thread.

No comments yet - start the thread.

Leave a comment

Comments are published immediately after submission.