概述
不知道大家有没有尝试上篇中所用的一些聚合查询,或者项目开发中有没有用过,我们会发现这些聚合几乎都是近似值,有些和我们预想的结果偏差较大。这是什么原因导致的呢?咱们开始揭秘
案例现象
我们将日志索引按照appId的方式进行分组查询前10个,DSL语句如下:
GET /log-server-query/_search
{
"size": 0,
"aggs": {
"NAME": {
"terms": {
"field": "appId",
"size": 10,
"show_term_doc_count_error": true
}
}
}
}
返回结果:
"aggregations" : {
"NAME" : {
"doc_count_error_upper_bound" : 191,
"sum_other_doc_count" : 37140,
"buckets" : [
{
"key" : "APP_C5BA55BDAD9C4E21BF306B9143419BDC",
"doc_count" : 1472101,
"doc_count_error_upper_bound" : 183
},
{
"key" : "APP_6AC648E51C64419AB7740F47D40FFFA6",
"doc_count" : 65643,
"doc_count_error_upper_bound" : 119
},
{
"key" : "APP_5BB849339B4A4FBBB6200312740CEEBE",
"doc_count" : 65205,
"doc_count_error_upper_bound" : 0
}........
]
}
}
可以明显看出来 doc_count_error_upper_bound的值最大到了183个,这个参数什么意思呢 ?它是通过show_term_doc_count_error来显示的,告知我们最差的情况下误差值,可以根据此值来调整shard_size,直到所有数据的doc_count_error_upper_bound都为0时此时聚合结果最为精确,但是再海量数据下也带了内存消耗问题。
问题原因
为什么出现这种问题呢?这就要从es的设计架构来说了,es7.x之前默认是5个主分片 1个副本,7.x以后默认1个主分片 但是实际研发中我们会根据数据的情况调整索引的分片数量,官方建议单个shard控制在50GB以内。
现在我们想要查询一个appid为APP_C5BA55BDAD9C4E21BF306B9143419BDC的数据 是需要所有分片都查询一遍么?如果这个数据就在第二个分片上呢?那遍历第一个shard的时间纯属浪费啊。es并不傻,他呢在数据写入的时候就已经将数据按照一定的规则来分片了,什么规则呢?是通过es的id进行hash取模。这样当有查询请求的时候特别是根据主键查询的时候性能就非常的快。
说到这里大家应该明白了把,这个和mysql的分库分表后的查询是一样的道理,如果sql上携带了分片建就执行单播sql,否则进行广播sql,那这个和聚合又有什么关系呢?
这个仍然以mysql分库分表为例来解释es,mysql分库分表以后聚合排序分页也是不准的,因为mysql无法将2个实例的数据进行聚合查询,只能先在单实例单库上进行聚合取给定范围的值 再返回到中间proxy层面二次进行聚合分页。解释到这里你应该就明白了es的问题了。
解决方案
第一种方案:可以通过show_term_doc_count_error来调试出shard_size 的合适值,通过指定shard_size来提高聚合的精确度,官方推荐:size*1.5+10 , 但是需要注意的是这种情况比较消耗内存, 例如:
GET /log-server-query/_search
{
"size": 0,
"aggs": {
"NAME": {
"terms": {
"field": "appId",
"size": 10,
"shard_size": 183,
"show_term_doc_count_error": true
}
}
}
}
返回:
"aggregations" : {
"NAME" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 37128,
"buckets" : [
{
"key" : "APP_C5BA55BDAD9C4E21BF306B9143419BDC",
"doc_count" : 1472101,
"doc_count_error_upper_bound" : 0
},
{
"key" : "APP_6AC648E51C64419AB7740F47D40FFFA6",
"doc_count" : 65655, 明显此值比之前大了
"doc_count_error_upper_bound" : 0
},
{
"key" : "APP_5BB849339B4A4FBBB6200312740CEEBE",
"doc_count" : 65205,
"doc_count_error_upper_bound" : 0
}......
]
}
第二种方案:将所有数据保存到一个shard中,此时就不存在该问题,但是只适用于小数据量的情况下。
第三种方案:将size调整到最大值,设置为2的32次方减1也就是shard支持的最大值,如果分片数据量很大的情况下会消耗巨大的CPU资源来做排序,可能出现网络带宽打满的情况。
Elasticsearch系列经典文章