[{"data":1,"prerenderedAt":17},["ShallowReactive",2],{"api:\u002Fapi\u002Fconfig:ru":3,"api:\u002Fapi\u002Fnotes\u002Fp95-nearest-rank:ru":8},{"email":4,"lang":5,"name":6,"role":7},"hello@ewsirdev.example","ru","Ewsirdev","Веб-разработчик: сайты, лендинги и сервисы на Go",{"slug":9,"lang":5,"title":10,"date":11,"summary":12,"project":13,"minutes":14,"body_md":15,"body_html":16},"p95-nearest-rank","p95 в мониторе доступности: метод ближайшего ранга и чего он не скрывает","2026-10-20","Как uptime-monitor считает p50\u002Fp95 задержки и аптайм, почему выбран метод ближайшего ранга и где эти цифры обманчивы.","uptime-monitor",1,"Монитор показывает p50 и p95 задержки успешных проверок за 24 часа. Среднее здесь плохо подходит: один выброс сдвигает его, а медленный хвост пользователи как раз и чувствуют.\n\n## Ближайший ранг\n\nПроцентиль p считается как элемент отсортированного списка с номером `ceil(p\u002F100 * N)`. Для 20 измерений p95 равен 19-му по возрастанию. Никакой интерполяции: результат всегда реальное измерение.\n\n```go\nrank := int(math.Ceil(p \u002F 100 * float64(len(s))))\nreturn s[rank-1]\n```\n\nФункция чистая: срез не меняется, для пустого среза вернётся 0, границы p\u003C=0 и p>=100 обработаны отдельно. Так её легко покрыть тестами.\n\n## Аптайм\n\nАптайм это доля успешных проверок, `ok \u002F total * 100`. Важная оговорка из README: он считается по **числу проверок**, а не по длительности, поэтому у проверок с разным интервалом цифры несопоставимы по точности.\n\n## Где цифры обманывают\n\n- На малом числе измерений p95 почти равен максимуму.\n- Агрегаты считаются запросами по сырой истории: для десятков проверок это мгновенно, для тысяч понадобятся предагрегаты.\n- Проверки идут с одной машины, это не распределённый мониторинг из нескольких регионов.\n","\u003Cp>Монитор показывает p50 и p95 задержки успешных проверок за 24 часа. Среднее здесь плохо подходит: один выброс сдвигает его, а медленный хвост пользователи как раз и чувствуют.\u003C\u002Fp>\n\u003Ch2>Ближайший ранг\u003C\u002Fh2>\n\u003Cp>Процентиль p считается как элемент отсортированного списка с номером \u003Ccode>ceil(p\u002F100 * N)\u003C\u002Fcode>. Для 20 измерений p95 равен 19-му по возрастанию. Никакой интерполяции: результат всегда реальное измерение.\u003C\u002Fp>\n\u003Cpre tabindex=\"0\">\u003Ccode class=\"lang-go\">rank := int(math.Ceil(p \u002F 100 * float64(len(s))))\nreturn s[rank-1]\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Функция чистая: срез не меняется, для пустого среза вернётся 0, границы p&lt;=0 и p&gt;=100 обработаны отдельно. Так её легко покрыть тестами.\u003C\u002Fp>\n\u003Ch2>Аптайм\u003C\u002Fh2>\n\u003Cp>Аптайм это доля успешных проверок, \u003Ccode>ok \u002F total * 100\u003C\u002Fcode>. Важная оговорка из README: он считается по \u003Cstrong>числу проверок\u003C\u002Fstrong>, а не по длительности, поэтому у проверок с разным интервалом цифры несопоставимы по точности.\u003C\u002Fp>\n\u003Ch2>Где цифры обманывают\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>На малом числе измерений p95 почти равен максимуму.\u003C\u002Fli>\n\u003Cli>Агрегаты считаются запросами по сырой истории: для десятков проверок это мгновенно, для тысяч понадобятся предагрегаты.\u003C\u002Fli>\n\u003Cli>Проверки идут с одной машины, это не распределённый мониторинг из нескольких регионов.\u003C\u002Fli>\n\u003C\u002Ful>\n",1792697892639]