histogram_quantileはどのようにquantileを計算しているか
Prometheusには、quantileを計測する方法としてSummaryとHistogramの2種類があります。
上記公式documentに記載がある通り、Summaryはclient sideでquantileを計測するのに対し、Histogramではprometheus sideでqueryを打つ際にアドホックに算出します。
Summaryはqueryの際にaggregationすることはできないため、たとえば「複数台のAPIサーバーがあり、それら全体での99th percentile response latencyをモニタリングしたい」といった場合は必然的にHistogramを選択することになります。(「1台1台からreportされた99th percentileの平均」などは、それはそれでモニタリングする価値のある指標ではあるかもしれませんが、統計的に意味をもつ値ではありません)
Histogramの使い方は次のようになります。(prometheus clientを使う場合はbuilderで簡単にセットアップできると思いますが)
- histogram bucketをどのように取るか決める。たとえば次のような具合。
[0, 100ms]
[0, 200ms]
[0, 300ms]
[0, 400ms]
[0, +∞]
- event発生時、対応するbucketの値をincrementする
- たとえばresponse latencyが256msだった場合、
[0, 300ms]
,[0, 400ms]
,[0, +∞]
の3つをincrementする
- たとえばresponse latencyが256msだった場合、
- それぞれのbucketについて、
le=<upper bound of bucket>
というlabelをつけてcountをexportする histogram_quantile
関数を使ってquantileを計算する。たとえば直近10minにおける99th percentileを計算したい場合は以下のようになる。histogram_quantile(0.99, sum(rate(api_latency_seconds_bucket[10m])) by (le))
How histogram_quantile calculates quantile from buckets
さて、histogram_quantile
はどのようにquantileを算出しているのでしょうか。
上記公式documentには
it applies linear interpolation
とあります。
さきほどの、0から400msまで100msごとに刻んでいる例を使って図示してみます。
直近10minで合計20回のAPIリクエストがあり、latencyの分布が以下のようになったとします。(もちろんhistogramに記録した時点で個々の点のlatencyは消滅し、実際に取れるのはbucketごとの4,8,12,...というcountだけです)
ここで75th-percentileを計算することを考えます。これは全体を小さい方から並べたときに75%のところに位置する値ですから、今回の場合小さいほうから数えて15番目の値ということです。
これより75th percentileが[300ms, 400ms]のどこかに位置することは分かりますが、個々の点の情報はすでに消滅してhistogramになってるため、近似するしかありません。
bucket内に一様にlatencyが分布していると仮定して線形補間すれば、75th percentile = 375msが得られます。
Prometheusのコードを見ると、まさにこのような計算を行なっていることが確認できます。
prometheus/quantile.go at v2.20.1 · prometheus/prometheus · GitHub
もちろん次のように分布が一様でない場合は近似の精度が下がることになりますが、それでもbucketの[lower bound, uppder bound]内に正確な値があることは保証できます。
そして、histogramはただのbucketごとのcounterですので、それぞれのserverからreportされた値をaggregateして全体でのquantileを計算することも容易です。