您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
【问题排查篇】一次业务问题对 ES 的 cardinality 原理探究
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
【问题排查篇】一次业务问题对 ES 的 cardinality 原理探究
15****
2023-04-27
IP归属:北京
20640浏览
## 业务问题 小编工作中负责业务的一个服务端系统,使用了 Elasticsearch 服务做数据存储,业务运营人员反馈,用户在使用该产品时发现,**用户后台统计的订单笔数和导出的订单笔数不一致**! 交易订单笔数不对,出现差错订单了?这一听极为震撼!出现这样的问题,在金融科技公司里面是绝对不允许发生的,得马上定位问题并解决! ![线上反馈业务数据查询和导出数据不一致](https://storage.jd.com/shendengbucket1/2022-07-23-14-25ArsJibuvOA0MxV2.png) 小编马上联系业务和相关人员,通过梳理上游系统的调用关系,发现业务系统使用到的是我这边的 ES 的存储服务,然后对线上情况进行复现,基本了解问题的现象: 1. 用户操作后台里的订单总笔数:商户页面的"订单总笔数","订单总笔数"使用的是小编 ES 存储服务中 ES 的统计聚合功能,其中订单总笔数是使用了 cardinality 操作,并且使用的是 orderId(订单编号)进行统计去重。 2. 导出功能里的订单总笔数:导出功能使用的是 ES 存储服务中的 ES 条件查询功能,导出功能是进行分页查询的。 ## 问题定位 这两个查询数量不一致,首先看查询条件是否一致呢? 经过一番排查,业务系统在调用查询订单总数和导出订单总数的这两个查询条件是一致的,也就是请求到我这边 ES 服务时,统计聚合的查询和分页导出的查询条件是一致的,但是为什么会在 ES 里面查询的结果是不一致的呢?难道 ES 里面的数据不全?统计聚合或分页导出的其中有一个不准了? 为了具体排查哪个操作可能存在问题,于是通过相同条件下查询数据库的总数和 ES 里面的数据进行对比。**发现相同条件下,数据库里面的数据和 ES 条件查询的总数是一致的, 同时业务的 orerId 字段是没有重复,所以可以确定的是:通过 orderId 进行统计聚合去重的操作是有问题的。** ![数据库查询数量](https://storage.jd.com/shendengbucket1/2022-07-23-14-27eQIZcuXJUVl23JUD.png) ![运营后台查询数量](https://storage.jd.com/shendengbucket1/2022-07-23-14-28ocwaWgadB28oKqjZ.png) > 数据库查询:数据库是做分库分表,此处数据库查询使用的是公司内的数据部银河大表——公司数据部会 T+1日从业务从库数据库中抽取 T 日的增量数据放在建立的"大表"中, 方便各业务进行数据使用。 > 运营后台查询:运营后台查询是直接查询 ES 存储服务。 数据部大表数量 = MySQL 数据库分库分表表里数量 = 运营控制台查询数量 = ES 存储文档数量 **问题定位:** **ES 存储服务对外给业务提供的: 通过 orderId 进行统计聚合去重(cardinality)的功能应该是有问题的。** ## ES 的 cardinality 原理探究 上面说过,小编负责的 ES 存储服务对外给业务提供了通过指定业务字段进行统计聚合去重的功能,统计聚合去重使用的是 ES 的 cardinality 功能。通过业务的查询的条件,使用 ES 的聚合功能 cardinality 操作,映射到 ES 层的操作命令如下代码所示, 执行业务的查询条件操作, **从 ES 的管理端后台里面查询竟然复现了和线上生产一样的结果,聚合统计的是 21514,条件查询的是 21427!!!** 可以确定的就是这个 cardinality 操作,导致了两个查询的数据不一致,如下图所示: ```json GET datastore_big_es_1_index/datastore_big_es_1_type/_search { "size": 3, "query": { "bool": { "must": [ { "match": { "v021.raw": "selfhelp" } }, { "match": { "v012.raw": "1001" } }, { "match": { "typeId": "00029" } }, { "range": { "createdDate": { "gte": "2021-02-01", "lt": "2021-03-01" } } }, { "bool": { "should": [ { "match": { "v031.raw": "113692300" } } ] } } ] } }, "aggs": { "distinct_orderId": { "cardinality": { "field": "v033.raw" } } } } ``` ![ES集群控制台cardinality操作](https://storage.jd.com/shendengbucket1/2022-07-23-14-3169qipmEG08Hbg88.png) **为什么 cardinality 操作会出现这样的结果呢?** 小编开始陷入了想当然的陷阱—— 以为这就是一个简简单单的统计去重的功能,ES 做的多好,帮你去重并统计数量了。然后事实并不是,通过 Elasticsearch 对 cardinality 官方文档解释,终于找到了原因。 > 可以参考Elasticsearch 2.x 版本官方文档对 cardinality的解释:[cardinality](https://www.elastic.co/guide/cn/elasticsearch/guide/current/cardinality.html) 其中对 cardinality 算法核心解释是: ![ES文档中对cardinality算法介绍](https://storage.jd.com/shendengbucket1/2022-07-23-14-32RUrYbq8TODY0OBG.png) **可以总结如下:** 1. **cardinality 并不是像关系型数据库 MySQL 一样精确去重的,cardinality做的是一个近似值,是 ES 帮你"估算"出的,这个估算使用的 **[HyperLogLog++](http://static.googleusercontent.com/media/research.google.com/en//pubs/archive/40671.pdf)** (HLL)算法,在速度上非常快,遍历一次即可统计去重,具体可看文档中推荐的论文**。 2. **ES 做cardinality估算,是可以设置估算精确度,即设置参数 precision_threshold 参数,但是这个参数在 0-40000, 这个值越大意味着精度越高,同时意味着损失更多的内存,是以内存空间换精度。** 3. **在小数据量下,ES 的这个"估算"精度是非常高的,几乎可以说是等于实际数量。** ## ES 中 cardinality 参数验证 下面对 ES 的 cardinality 的 **precision_threshold** 参数进行验证: **1、大数据量下,设置最高精度及其以上,仍然会存在误差:** ![大数据量下,设置percision_threshold高精度值验证](https://storage.jd.com/shendengbucket1/2022-07-23-14-39Ks23vwtOP9S0mh23f.png) **2、小数据量下,设置最高精度,可以和实际数量保持一致:** ![小数据量下,设置percision_threshold高精度验证](https://storage.jd.com/shendengbucket1/2022-07-23-14-41UOqdnld97C6Ykkv.png) 那么线上的为什么聚合统计的是 21514,条件查询的是 21427? 线上代码运行和ES集群设置都没有主动设置过 precision_threshold 参数,那么可以知道,这个应该是 ES 集群设置的默认值。**线上 ES 集群版本为 5.4x 因此找到 5.4 版本的官方文档,发现 5.4 版本中设置的是默认值 precision_threshold=3000**, 在此条件下查询的统计聚合出来的值是 21514。 另外 ES 官方对 cardinality 操作中的 **precision_threshold** 参数也做了研究,研究了官方文档中**precision_threshold设置**和**cardinality查询失败率**、**查询数据量级**的关系,可作为我们在业务开发中进行参考,如下图所示: ![官方文档中precision_threshold设置和cardinality查询失败率的关系研究](https://storage.jd.com/shendengbucket1/2022-07-23-14-43LCHh7lbC43LXeuvB.png) > Elasticsearch 5.4版本官方文档对cardinality中**precision_threshold**参数的研究文档:**[precision_threshold](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-cardinality-aggregation.html)** ## 总结与方案 通过对 cardinality 的原理探究, 需要明白的是 : 我们使用 cardinality 是需要区分使用场景的。 1. **对于精确统计的业务场景,是不建议使用的**。 例如:订单数的统计(统计结果会引起歧义)的场景下,不建议使用。 2. **对于非精确统计的业务场景,那么可以说是很有用了,尤其是在大数据量的场景下,在保持一定的准确性下,同时能提供高性能。** 例如:监控指标数据,大盘比例计算等场景,在非精确统计下,是有很大用处。 基于小编的这个业务场景,对商户订单进行统计,是属于精确统计场景,那 cardinality 操作就不适合了。又因为业务的 orderId 是不会重复的,理论上在我们 ES 集群中每个记录的 orderId 都是唯一的,因此可以不用进行去重,而可以直接使用 ES 的 count 操作,将订单数统计汇总出,对应 Elasticsearch 开发包中 COUNT API 如下: ```java org.springframework.data.elasticsearch.core.ElasticsearchTemplate #count(org.springframework.data.elasticsearch.core.query.SearchQuery, java.lang.Class<T>) ``` ```java public <T> long count(SearchQuery searchQuery, Class<T> clazz) { QueryBuilder elasticsearchQuery = searchQuery.getQuery(); QueryBuilder elasticsearchFilter = searchQuery.getFilter(); return elasticsearchFilter == null ? this.doCount(this.prepareCount(searchQuery, clazz), elasticsearchQuery) : this.doCount(this.prepareSearch(searchQuery, clazz), elasticsearchQuery, elasticsearchFilter); } ``` *** > 本文已对敏感信息脱敏处理 最后欢迎大家点赞、收藏、评论,转发!❤️❤️❤️
上一篇:【架构与设计】常见微服务分层架构的区别和落地实践
下一篇:“前端”工匠系列(一):合格的工匠,究竟该搞什么
15****
文章数
4
阅读量
1456
作者其他文章
01
【问题排查篇】长连接Netty服务内存泄漏,看我如何一步步捉“虫”解决
背景事情要回顾到双11.11备战前夕,在那个风雨交加的夜晚,一个急促的咚咚报警,惊破了电闪雷鸣的黑夜,将沉浸在梦香,熟睡的我惊醒。一看手机咚咚报警,不好!有大事发生了!电话马上打给老板:老板说: 长连接吗?我说:是的!老板说:该来的还是要来的,最终还是来了,快,赶紧先把服务重启下!我说:已经重启了!老板说: 这问题必须给我解决了!我说:必须的!线上应用长连接Netty服务出现内存泄漏了!真让人头大
01
【问题排查篇】一次业务问题对 ES 的 cardinality 原理探究
业务问题小编工作中负责业务的一个服务端系统,使用了 Elasticsearch 服务做数据存储,业务运营人员反馈,用户在使用该产品时发现,用户后台统计的订单笔数和导出的订单笔数不一致!交易订单笔数不对,出现差错订单了?这一听极为震撼!出现这样的问题,在金融科技公司里面是绝对不允许发生的,得马上定位问题并解决!小编马上联系业务和相关人员,通过梳理上游系统的调用关系,发现业务系统使用到的是我这边的 E
01
【翻译】Netty的对象引用计数
从Netty 4版本开始,某些对象的生命周期由其引用计数管理,因此Netty可以在不再使用时立即将它们(或共享资源)返回到对象池中(或对象分配器)。垃圾回收和引用队列不能提供无法访问的有效实时保证,但是引用计数提供了一种替代机制,代价是有些轻微的不便。ByteBuf是最值得注意的类型,它利用引用计数来提高内存分配和释放内存的性能,本文档将解释Netty中的ByteBuf如何使用引用计数来工作的。引
15****
文章数
4
阅读量
1456
作者其他文章
01
【问题排查篇】长连接Netty服务内存泄漏,看我如何一步步捉“虫”解决
01
【翻译】Netty的对象引用计数
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号