Среднее лжёт, перцентиль тоже
Почему перцентили нельзя усреднять, почему задержка взрывается задолго до полной загрузки и что такое согласованное умолчание в нагрузочном замере.
Коротко. Средняя задержка не описывает ничего. Но и перцентиль на панели чаще всего посчитан неверно: усреднён по экземплярам или занижен согласованным умолчанием в нагрузочном инструменте.
«Средняя задержка сорок миллисекунд» — величина, которая не отвечает ни на один вопрос. Она совместима и с ровной работой, и с картиной, где девяносто процентов запросов идут за пять миллисекунд, а десять — за четыреста.
Это знают, и вместо среднего смотрят на перцентили. Дальше начинаются три ошибки, каждая из которых искажает результат сильнее, чем само среднее.
Перцентили нельзя усреднять
Самая частая. Пять экземпляров сервиса, у каждого своя 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.5 | 1 × время обслуживания |
| 0.8 | 4 × |
| 0.9 | 9 × |
| 0.95 | 19 × |
| 0.99 | 99 × |
Рост не линейный, а гиперболический. Между 50 и 80 процентами загрузки ожидание вырастает вчетверо, между 90 и 99 — на порядок.
Модель идеализирована: она предполагает пуассоновский поток и экспоненциальное обслуживание. Реальный трафик даёт всплески, а реальное обслуживание — тяжёлый хвост, и оба отклонения делают картину хуже, а не лучше. Поэтому формулу стоит читать как оптимистичную границу.
Практический вывод: планировать загрузку выше семидесяти процентов по среднему — значит соглашаться на задержку, которая живёт на крутом участке кривой и реагирует на любой всплеск непропорционально.
Согласованное умолчание
Самая коварная из ошибок, потому что она в самом инструменте замера.
Нагрузочный генератор рассылает запросы и ждёт ответа перед отправкой следующего. Сервис встаёт на секунду. Генератор в это время не отправляет ничего — он ждёт. Секундная остановка попадает в выборку одним измерением.
А в реальности за эту секунду пришли бы все запросы, которые должны были прийти по расписанию, и каждый из них ждал бы своей доли этой секунды. Правильная выборка содержала бы сотни замеров с задержками от секунды вниз.
Джил Тене назвал это согласованным умолчанием: инструмент «сговаривается»
с системой и не измеряет именно те моменты, ради которых замер делался.
Занижение приходится целиком на хвост — на p99 и выше, то есть ровно
там, где смотрят.
Лечится двумя способами. Либо генератор отправляет по расписанию,
не подстраиваясь под ответы, и учитывает полное время от намеченного
момента отправки. Либо результат корректируется постфактум — так делает
HdrHistogram, дописывая пропущенные измерения.
Что я держу на панели
Гистограмму задержки, из которой считаются p50, p95, p99, и всегда
рядом — частоту запросов. Перцентиль без частоты не читается: p99
на десяти запросах в минуту и на десяти тысячах в секунду — величины
разной надёжности.
Отдельно — закон Литтла как проверка на здравый смысл:
L = λ · W
Среднее число запросов в системе равно произведению частоты на среднее время пребывания. Соотношение точное и не зависит от распределений. Оно позволяет вывести третью величину из двух: если известно, что в пуле двадцать соединений и запрос живёт двести миллисекунд, то выше ста запросов в секунду система не поднимется, сколько бы воркеров ни добавили.
Когда этим можно не заниматься. Сервис, где задержка на порядок
меньше допустимой, а загрузка держится в районе десяти процентов, живёт
на пологой части кривой, и разница между средним и p99 там несущественна.
Разговор начинается, когда загрузка подошла к двум третям.