Алексей Меленчук
вся записка
дата
объём 5 мин чтения

Среднее лжёт, перцентиль тоже

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

Коротко. Средняя задержка не описывает ничего. Но и перцентиль на панели чаще всего посчитан неверно: усреднён по экземплярам или занижен согласованным умолчанием в нагрузочном инструменте.

«Средняя задержка сорок миллисекунд» — величина, которая не отвечает ни на один вопрос. Она совместима и с ровной работой, и с картиной, где девяносто процентов запросов идут за пять миллисекунд, а десять — за четыреста.

Это знают, и вместо среднего смотрят на перцентили. Дальше начинаются три ошибки, каждая из которых искажает результат сильнее, чем само среднее.

Перцентили нельзя усреднять

Самая частая. Пять экземпляров сервиса, у каждого своя p99, на панели показано их среднее.

Это не p99 системы. Это среднее пяти чисел, не имеющее статистического смысла: p99 объединения выборок не выражается через p99 частей.

Крайний пример показывает, почему. Четыре экземпляра обслуживают по одному запросу в секунду и отвечают мгновенно, у каждого p99 = 1 мс. Пятый обслуживает тысячу запросов в секунду с p99 = 500 мс. Среднее пяти значений — около ста миллисекунд. Настоящий p99 всей системы близок к пятистам, потому что подавляющее большинство запросов проходит через пятый экземпляр.

Считать перцентиль можно только по объединённой выборке — либо по гистограмме, которая складывается. Именно поэтому системы метрик хранят задержку гистограммой с фиксированными корзинами: корзины складываются между экземплярами, а перцентиль считается уже по сумме.

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

Перцентиль пользователя — не перцентиль запроса

Второе, что упускают. Страница делает не один запрос.

Если страница выполняет n независимых запросов, а доля медленных — один процент, то вероятность, что ни один не окажется медленным, равна 0.99^n. При n = 50 это примерно 0.605.

То есть примерно сорок процентов загрузок страницы зацепят хотя бы один запрос из «худшего процента». p99 на уровне запроса — это далеко не p99 на уровне пользователя.

Отсюда вывод, который меняет приоритеты: для страницы с десятками обращений хвост распределения важнее середины, и работа над p99 окупается сильнее, чем работа над медианой.

Загрузка и очередь

Третье — не ошибка измерения, а непонимание природы величины.

Вопрос «почему при загрузке восемьдесят процентов задержка выросла втрое, хотя запас ещё есть» имеет точный ответ в теории массового обслуживания. Для простейшей модели M/M/1 среднее время ожидания в очереди относится к времени обслуживания как

W / S = ρ / (1 − ρ)

где ρ — коэффициент загрузки. Подставим:

ЗагрузкаОжидание в очереди
0.51 × время обслуживания
0.84 ×
0.99 ×
0.9519 ×
0.9999 ×

Рост не линейный, а гиперболический. Между 50 и 80 процентами загрузки ожидание вырастает вчетверо, между 90 и 99 — на порядок.

Модель идеализирована: она предполагает пуассоновский поток и экспоненциальное обслуживание. Реальный трафик даёт всплески, а реальное обслуживание — тяжёлый хвост, и оба отклонения делают картину хуже, а не лучше. Поэтому формулу стоит читать как оптимистичную границу.

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

Согласованное умолчание

Самая коварная из ошибок, потому что она в самом инструменте замера.

Нагрузочный генератор рассылает запросы и ждёт ответа перед отправкой следующего. Сервис встаёт на секунду. Генератор в это время не отправляет ничего — он ждёт. Секундная остановка попадает в выборку одним измерением.

А в реальности за эту секунду пришли бы все запросы, которые должны были прийти по расписанию, и каждый из них ждал бы своей доли этой секунды. Правильная выборка содержала бы сотни замеров с задержками от секунды вниз.

Джил Тене назвал это согласованным умолчанием: инструмент «сговаривается» с системой и не измеряет именно те моменты, ради которых замер делался. Занижение приходится целиком на хвост — на p99 и выше, то есть ровно там, где смотрят.

Лечится двумя способами. Либо генератор отправляет по расписанию, не подстраиваясь под ответы, и учитывает полное время от намеченного момента отправки. Либо результат корректируется постфактум — так делает HdrHistogram, дописывая пропущенные измерения.

Что я держу на панели

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

Отдельно — закон Литтла как проверка на здравый смысл:

L = λ · W

Среднее число запросов в системе равно произведению частоты на среднее время пребывания. Соотношение точное и не зависит от распределений. Оно позволяет вывести третью величину из двух: если известно, что в пуле двадцать соединений и запрос живёт двести миллисекунд, то выше ста запросов в секунду система не поднимется, сколько бы воркеров ни добавили.

Когда этим можно не заниматься. Сервис, где задержка на порядок меньше допустимой, а загрузка держится в районе десяти процентов, живёт на пологой части кривой, и разница между средним и p99 там несущественна. Разговор начинается, когда загрузка подошла к двум третям.