Elasticsearch 聚合学习之五:排序结果不准的问题分析
欢迎访问我的 GitHub
这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos
欢迎访问我的 GitHub
这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos
本篇概览
Elasticsearch 上的索引如果有多个分片,那么在聚合排序后取 TopN 时,返回的结果可能是不准的,今天我们就通过实战来研究分析此问题,并验证解决方法;
环境信息
以下是本次实战的环境信息,请确保您的 Elasticsearch 可以正常运行:
操作系统:Ubuntu 18.04.2 LTS
JDK:1.8.0_191
Elasticsearch:6.7.1
Kibana:6.7.1
系列文章列表
复现问题第一步:创建索引
首先是将问题复现,这里我做了个简单的索引,只有两个字段,将索引分为两个分片,然后准备了一些数据写入这两个分片;
在 Kibana 的 Dev Tools 执行以下命令,即可创建名为 taskcase 的索引,该索引有两个分片,只有两个字段:name 和 value:
接下来是导入数据了。
复现问题第二步:导入数据
为了测试的准确性,按照以下要求来制造测试数据:
按照 name 字段聚合,name 的值不宜太多,否则会有过多的桶不好分析结果;
能精确的指定哪些数据到分片 1,哪些到分片 2;
对于这份测试数据,这里先给出聚合结果(在生成数据的时候计算出来的),有了这些结果,我们就能和 es 聚合结果做对比,发现问题所在:分片一,按 name 聚合后,name 相同的文档 value 字段之和:
分片二,按 name 聚合后,name 相同的文档 value 字段之和:
所有数据,按 name 聚合后,name 相同的文档 value 字段之和:
这份数据集保存在 bulk.json 文件中,您可以在此下载:https://raw.githubusercontent.com/zq2599/blog_demos/master/files/bulk.json
下载后,用 curl 命令导入这些数据:
在 bulk.json 中,由 routing 的值来决定数据会存在哪个分片中,已经验证过 routing=a 时会写入第一个分片,routing=b 时写入第二个分片,因此整个 bulk.json 中的 routing 的值只有 a 和 b 两种;
上述数据和统计结果都是用 java 生成的,对应的源码地址在此:https://raw.githubusercontent.com/zq2599/blog_demos/master/files/GenerateESAggSortData.java
现在数据已经准备好了,可以复现问题了;
复现问题
导入数据成功后,执行以下命令,按照 name 做聚合,将 name 相同的文档的 value 字段的值相加:
得到的结果如下:
问题已经出现了,返回的数据中,第四名的 name 是 15,但实际上 19 才是第四名,对比列表如下:
分析问题
在聚合排序的操作中,实际上是每个分片自身先做排序,然后将每个分片的前 17 名放在一起再次聚合,再排序,将排序后的前 5 条记录作为结果返回;
为什么用每个分片的前 17 名?这是用官方给出的算式得来的,地址是:https://www.elastic.co/guide/en/elasticsearch/reference/6.1/search-aggregations-bucket-terms-aggregation.html ,如下图:
如果请求只发往一个分片,就返回前 5 条,如果发往多个分片,每个分片返回的条数是:5*1.5+10=17
用一幅图来描述,如下图,汇总数据中的紫色,是由分片一和分片二中的紫色合成的:
如上图所示,分片一的前 17 条记录中,没有 name 等于 19 的记录(因为该记录在分片一的排名是 28),所以两个分片的数据聚合后,name 等于 19 的记录只有分片二的数据中有,即 19:168589,这个值在汇总数据中是排不上前 5 的,于是 ES 返回的 Top5 与真实数据的 Top5 就不一样了,这就是 Elasticsearch 聚合后排序不准的原因。
接下来看看如何解决此问题
解决办法之一
知道问题的原因解决起来就容易了:如果每个分片返回的不是前 17 名,而是前 28 名,那么两个分片中都含有 name 等于 19 的记录,这个指定分片返回数量的参数是 shard_size,加上 shard_size 参数的整个请求如下:
得到结果如下,与真实排名一致:
由此可以看出:shard_size 越大,得到的结果准确率越高,如果 shard_size 不低于桶的数量,那么就是准确值了。
但实际生产环境中需要结合实际情况来设置 shard_size,因为该值越大汇总的数据量就越大,对网络、内存等资源的消耗都会增加,会影响整体性能;
解决办法之二
第二种解决方式就是所有的数据都在一个分片上,具体的方法是创建索引时分片数设置为 1,或者在增加数据时指定 routing,并且查询的时候也使用该 routing,这些方法您可以自行验证,创建一个分片的索引的脚本如下:
至此,关于聚合排序不准的实战和分析就全部完成了,在您使用 es 的聚合后 TopN 时如果遇到类似问题,希望此文能够给您提供一些参考;
欢迎关注 InfoQ:程序员欣宸
版权声明: 本文为 InfoQ 作者【程序员欣宸】的原创文章。
原文链接:【http://xie.infoq.cn/article/459536ada13dee9e20443a820】。文章转载请联系作者。
评论