Posted at

Elasticsearch で percentile 計算

More than 5 years have passed since last update.

Elasticsearch 1.3 が出たと聞いて、そういえば percentile に対応するらしいって前に聞いたのを思い出してググったら 1.1 (2014年3月) で既に対応済みでした。(Experimental ですけどね)

Percentiles Aggregation

ということなので早速試してみます。

データは Kibana 用にインデックス済みなのでこいつを使います。ウェブサービスのレスポンタイムのパーセンタイル値欲しいですよね。

まずは上記のドキュメントに書いてあるサンプルをひとつ。だたし、対象となったドキュメントの内容は不要なので size: 0 を指定しています。


query1.json

{

"aggs": {
"load_time_outlier": {
"percentiles": {
"field": "responseTime",
"percents": [
95,
99,
99.9
]
}
}
},
"size": 0
}

$ curl -s -X POST -d @query1.json \

http://elasticsearch:9200/accesslog-2014.07.25/_search?pretty


response

{

"took" : 121,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"failed" : 0
},
"hits" : {
"total" : 390458,
"max_score" : 0.0,
"hits" : [ ]
},
"aggregations" : {
"load_time_outlier" : {
"values" : {
"95.0" : 274762.7526741865,
"99.0" : 683982.277012403,
"99.9" : 2122798.5229873355
}
}
}
}

とっても簡単!!そして速い。でもこれだと、インデックス全体が対象となってしまうので5分単位、10分単位とか1時間単位で出したいですよね。

そこで、次のように range フィルターで時間を絞りましょう。


query2.json

{

"aggs": {
"load_time_outlier": {
"percentiles": {
"field": "responseTime",
"percents": [
95,
99,
99.9
]
}
}
},
"filter": {
"range": {
"@timestamp": {
"to": 1406250000000,
"from": 1406246400000
}
}
},
"size": 0
}

1時間おきならこの結果を HRForecast に突っ込めばいつでも参照できますね(私はまだ使ったことないですけど)。5分とかなら InfluxDB でも PostgreSQL でも MySQL にでも突っ込んで好きな時に参照できるようにすると良いですね。

でもまだ HTTP の status code は 200 に限定したいとか、画像などの静的ファイルは除外したいとかありますね。


query3.json

{

"aggs": {
"load_time_outlier": {
"percentiles": {
"field": "responseTime",
"percents": [
95,
99,
99.9
]
}
}
},
"query": {
"filtered": {
"query": {
"query_string": {
"query": "status:200 AND NOT uri:/.*\\.(png|gif|jpeg|jpg|css|js)/ AND NOT uri:/.*\\.(png|gif|jpeg|jpg|css|js)?.*/"
}
},
"filter": {
"range": {
"@timestamp": {
"to": 1406250000000,
"from": 1406246400000
}
}
}
}
},
"size": 0
}

こんな感じでしょうか。なんか美しくないけど...

uri には Apache の LogConfig での %U%q が入っているものとします。

便利です。