Коротко. Среднее время ответа описывает операцию, которой нет. p50 показывает обычную работу, p95 рабочую цель, p99 то, на что жалуются: при сотнях действий в день самый медленный процент видит каждый. Перцентили не усредняют, их считают по исходным замерам или по сложенным гистограммам, а p99 смотрят только на большой выборке.

На совещании показывают график: среднее время открытия документа одна секунда, почти не меняется месяц. А пользователи пишут, что документы открываются «по десять секунд». Обе стороны правы. Среднее время честно посчитано, но описывает работу, которой ни один пользователь не видит. Разбираем, что показывают перцентили (их ещё называют процентилями), какой из них за что отвечает и где их считают неправильно.

Симптом

  • По панелям мониторинга система быстрая, по обращениям пользователей медленная.
  • После оптимизации среднее время упало, а жалоб не стало меньше.
  • В отчёте нагрузочного теста всё зелёное, в продуктиве тормозит.
  • Время ответа на панели меняется в разы в зависимости от того, за какой период смотреть.

Почему так

Время ответа в любой системе распределено неравномерно. Большинство операций быстрые, а небольшая доля упирается в блокировку в базе, в холодный кэш, в сборку мусора, в очередь. Медленных мало, но они очень медленные, и они тянут среднее вверх ровно настолько, чтобы оно не совпало ни с быстрыми, ни с медленными.

Перцентиль отвечает на другой вопрос: какое время не превышают N процентов операций.

  • p50 (медиана): половина операций быстрее. Так система работает «обычно».
  • p90: девять из десяти операций быстрее. Хорошая граница для повседневного ощущения скорости.
  • p95: девятнадцать из двадцати. Самая употребимая цель: хвост уже виден, но шум ещё не мешает.
  • p99: девяносто девять из ста. Здесь живут блокировки, таймауты и всё, на что жалуются.

Модель для примера: 10 000 открытий документа, 96 % из них занимают около 0,7 секунды, 4 % упираются в блокировку и идут от 4 до 12 секунд. Это синтетические числа, но форма распределения типичная.

Показатель Значение
Среднее 1,00 с
p50 0,68 с
p90 1,15 с
p95 1,50 с
p99 9,75 с
Максимум 11,99 с

Среднее в одну секунду не описывает ни одну реальную операцию: 84 % открытий быстрее среднего, остальные медленнее в разы. А p99 почти в десять раз больше среднего и показывает то, что пользователи и называют «по десять секунд».

Почему p99 касается каждого. Кажется, что 1 % медленных операций это редкость. Но сотрудник за день делает сотни действий. При 200 действиях в день вероятность хотя бы раз попасть в самый медленный процент равна 87 %, а в самые медленные 5 % почти стопроцентная. Хвост распределения видит каждый пользователь, каждый день. Поэтому жалуются на p99, а не на среднее.

Три ошибки при подсчёте

1. Усреднять перцентили. Два сервера: на первом 9 000 операций с p95 1,19 секунды, на втором 1 000 операций, и пятая часть из них медленные, p95 10,3 секунды. Среднее двух p95 даёт 5,75 секунды. Настоящий p95 по всем операциям 1,31 секунды. Ошибка в четыре раза. Перцентиль по группе считают по всем исходным замерам сразу или по сложенным гистограммам, но не из перцентилей частей. Отдельные p95 по серверам при этом полезны сами по себе: в примере сразу видно, что второй сервер болен.

2. Считать p99 на малой выборке. Чтобы за 99-м перцентилем было хотя бы десять замеров, нужна тысяча операций. По 50 замерам p99 это фактически максимум, одно случайное значение. Для редких операций за час смотрят p90 или p95 или берут окно больше.

3. Не замечать, как устроены корзины гистограммы. Prometheus не хранит каждый замер, он хранит счётчики по корзинам: «быстрее 1 секунды», «быстрее 2,5», «быстрее 5», «быстрее 10». Функция histogram_quantile предполагает, что внутри корзины замеры распределены равномерно, и интерполирует. Если p95 попал в корзину от 5 до 10 секунд, показанные 7,3 секунды значат «где-то между 5 и 10». Корзины подбирают так, чтобы граница цели была границей корзины: если цель 2 секунды, корзина на 2 секунды должна быть.

Диагностика: где взять перцентили

По журналам RX в Elasticsearch. Если журналы RX собраны с полями операции и длительности (как это сделать, разобрано в статье про Vector), перцентили по каждой операции считаются одним запросом:

GET rx-logs-rx-*/_search
{
  "size": 0,
  "query": { "range": { "timestamp": { "gte": "now-1h" } } },
  "aggs": {
    "by_op": {
      "terms": { "field": "operation.keyword", "size": 20, "order": { "_count": "desc" } },
      "aggs": { "t": { "percentiles": { "field": "duration_ms", "percents": [50, 90, 95, 99] } } }
    }
  }
}

Перцентили в Elasticsearch приблизительные (алгоритм TDigest), но на краях распределения, где p95 и p99, точность высокая. Для разбора медленных операций этого достаточно.

По метрикам в Prometheus. Если сервис отдаёт гистограмму времени ответа, p95 за пять минут по всем экземплярам:

histogram_quantile(0.95, sum by (le) (rate(http_request_duration_seconds_bucket[5m])))

Сначала складываются корзины всех экземпляров (sum by (le)), потом считается перцентиль. По каждому экземпляру отдельно: sum by (le, instance).

В нагрузочном тесте. JMeter в отчёте и в Aggregate Report показывает колонки 90 %, 95 % и 99 % Line. Сравнивать версии и конфигурации нужно по ним, а не по Average. Как устроить сам тест, рассказано в статье про JMeter.

Что сделать

  1. Выбрать операции, которые важны пользователям: вход, открытие документа, сохранение карточки, поиск, выполнение задания. Не время ответа «вообще», а по каждой операции.
  2. Записать цели в перцентилях. Например: «p95 открытия документа за час не больше 2 секунд, p99 не больше 5». Цифры подбирают по текущим замерам и по тому, на что жалуются.
  3. Вывести на панель p50, p95 и p99 рядом. Расходятся p50 и p95: часть операций упирается во что-то общее. Растёт только p99: блокировки, таймауты, отдельные тяжёлые запросы.
  4. Алерт ставить на p95, а не на p99 и не на среднее. p99 на небольшом трафике шумит, среднее реагирует поздно. Правило вида «p95 открытия документа выше цели 15 минут подряд».
  5. Разбирать хвост поимённо. Перцентиль показывает, что медленно, но не почему. Дальше нужны сами медленные операции: какой метод, какой запрос к базе, какая блокировка. Порядок такого разбора описан в статье про поиск причины тормозов.

Чего не делать

  • Не отчитываться средним временем. Оно прячет ровно то, на что жалуются.
  • Не усреднять перцентили по серверам, дням или операциям. Считать по исходным замерам или по сложенным гистограммам.
  • Не смотреть p99 на сотне замеров. Это одно случайное значение.
  • Не сравнивать перцентили за разные окна. p95 за пять минут пика и p95 за сутки это разные числа.
  • Не гнаться за p99.9 на внутренней системе. Десятая доля процента это единичные события, их разбирают поимённо, а не графиком.