Skip to content

百分位度量

让我加载一个新的数据集(汽车的数据不太适用于百分位)。我们要索引一系列网站延时数据然后运行一些百分位操作进行查看:

json
POST /website/logs/_bulk
{ "index": {}}
{ "latency" : 100, "zone" : "US", "timestamp" : "2014-10-28" }
{ "index": {}}
{ "latency" : 80, "zone" : "US", "timestamp" : "2014-10-29" }
{ "index": {}}
{ "latency" : 99, "zone" : "US", "timestamp" : "2014-10-29" }
{ "index": {}}
{ "latency" : 102, "zone" : "US", "timestamp" : "2014-10-28" }
{ "index": {}}
{ "latency" : 75, "zone" : "US", "timestamp" : "2014-10-28" }
{ "index": {}}
{ "latency" : 82, "zone" : "US", "timestamp" : "2014-10-29" }
{ "index": {}}
{ "latency" : 100, "zone" : "EU", "timestamp" : "2014-10-28" }
{ "index": {}}
{ "latency" : 280, "zone" : "EU", "timestamp" : "2014-10-29" }
{ "index": {}}
{ "latency" : 155, "zone" : "EU", "timestamp" : "2014-10-29" }
{ "index": {}}
{ "latency" : 623, "zone" : "EU", "timestamp" : "2014-10-28" }
{ "index": {}}
{ "latency" : 380, "zone" : "EU", "timestamp" : "2014-10-28" }
{ "index": {}}
{ "latency" : 319, "zone" : "EU", "timestamp" : "2014-10-29" }

数据有三个值:延时、数据中心的区域以及时间戳。让我们对数据全集进行 百分位 操作以获得数据分布情况的直观感受:

json
GET /website/logs/_search
{
    "size" : 0,
    "aggs" : {
        "load_times" : {
            "percentiles" : {
                "field" : "latency" 
            }
        },
        "avg_load_time" : {
            "avg" : {
                "field" : "latency" 
            }
        }
    }
}

默认情况下,percentiles 度量会返回一组预定义的百分位数值: [1, 5, 25, 50, 75, 95, 99] 。它们表示了人们感兴趣的常用百分位数值,极端的百分位数在范围的两边,其他的一些处于中部。在返回的响应中,我们可以看到最小延时在 75ms 左右,而最大延时差不多有 600ms。与之形成对比的是,平均延时在 200ms 左右, 信息并不是很多:

json
...
"aggregations": {
  "load_times": {
     "values": {
        "1.0": 75.55,
        "5.0": 77.75,
        "25.0": 94.75,
        "50.0": 101,
        "75.0": 289.75,
        "95.0": 489.34999999999985,
        "99.0": 596.2700000000002
     }
  },
  "avg_load_time": {
     "value": 199.58333333333334
  }
}

所以显然延时的分布很广,让我们看看它们是否与数据中心的地理区域有关:

json
GET /website/logs/_search
{
    "size" : 0,
    "aggs" : {
        "zones" : {
            // 首先根据区域我们将延时分到不同的桶中。
            "terms" : {
                "field" : "zone" 
            },
            "aggs" : {
                "load_times" : {
                    // 再计算每个区域的百分位数值。
                    "percentiles" : { 
                      "field" : "latency",
                      // percents 参数接受了我们想返回的一组百分位数,因为我们只对长的延时感兴趣。
                      "percents" : [50, 95.0, 99.0] 
                    }
                },
                "load_avg" : {
                    "avg" : {
                        "field" : "latency"
                    }
                }
            }
        }
    }
}

在响应结果中,我们发现欧洲区域(EU)要比美国区域(US)慢很多,在美国区域(US),50 百分位与 99 百分位十分接近,它们都接近均值。

与之形成对比的是,欧洲区域(EU)在 50 和 99 百分位有较大区分。现在,显然可以发现是欧洲区域(EU)拉低了延时的统计信息,我们知道欧洲区域的 50% 延时都在 300ms+。

json
...
"aggregations": {
  "zones": {
     "buckets": [
        {
           "key": "eu",
           "doc_count": 6,
           "load_times": {
              "values": {
                 "50.0": 299.5,
                 "95.0": 562.25,
                 "99.0": 610.85
              }
           },
           "load_avg": {
              "value": 309.5
           }
        },
        {
           "key": "us",
           "doc_count": 6,
           "load_times": {
              "values": {
                 "50.0": 90.5,
                 "95.0": 101.5,
                 "99.0": 101.9
              }
           },
           "load_avg": {
              "value": 89.66666666666667
           }
        }
     ]
  }
}
...

百分位等级

这里有另外一个紧密相关的度量叫 percentile_ranks 。 percentiles 度量告诉我们落在某个百分比以下的所有文档的最小值。例如,如果 50 百分位是 119ms,那么有 50% 的文档数值都不超过 119ms。 percentile_ranks 告诉我们某个具体值属于哪个百分位。119ms 的 percentile_ranks 是在 50 百分位。 这基本是个双向关系,例如:

  • 50 百分位是 119ms。
  • 119ms 百分位等级是 50 百分位。 所以假设我们网站必须维持的服务等级协议(SLA)是响应时间低于 210ms。然后,开个玩笑,我们老板警告我们如果响应时间超过 800ms 会把我开除。可以理解的是,我们希望知道有多少百分比的请求可以满足 SLA 的要求(并期望至少在 800ms 以下!)。

为了做到这点,我们可以应用 percentile_ranks 度量而不是 percentiles 度量:

json
GET /website/logs/_search
{
    "size" : 0,
    "aggs" : {
        "zones" : {
            "terms" : {
                "field" : "zone"
            },
            "aggs" : {
                "load_times" : {
                    // percentile_ranks 度量接受一组我们希望分级的数值。
                    "percentile_ranks" : {
                      "field" : "latency",
                      "values" : [210, 800] 
                    }
                }
            }
        }
    }
}

在聚合运行后,我们能得到两个值:

json
"aggregations": {
  "zones": {
     "buckets": [
        {
           "key": "eu",
           "doc_count": 6,
           "load_times": {
              "values": {
                 "210.0": 31.944444444444443,
                 "800.0": 100
              }
           }
        },
        {
           "key": "us",
           "doc_count": 6,
           "load_times": {
              "values": {
                 "210.0": 100,
                 "800.0": 100
              }
           }
        }
     ]
  }
}

这告诉我们三点重要的信息:

  • 在欧洲(EU),210ms 的百分位等级是 31.94% 。
  • 在美国(US),210ms 的百分位等级是 100% 。
  • 在欧洲(EU)和美国(US),800ms 的百分位等级是 100% 。

通俗的说,在欧洲区域(EU)只有 32% 的响应时间满足服务等级协议(SLA),而美国区域(US)始终满足服务等级协议的。但幸运的是,两个区域所有响应时间都在 800ms 以下,所以我们还不会被炒鱿鱼(至少目前不会)。

percentile_ranks 度量提供了与 percentiles 相同的信息,但它以不同方式呈现,如果我们对某个具体数值更关心,使用它会更方便。

参考

https://www.elastic.co/guide/cn/elasticsearch/guide/current/percentiles.html