首先要阐述一个观点,任何技术都是为解决某一个领域的问题而存在的,我们在使用它的时候,尽可能使用它的优势(亮点),去发挥它应具备的业务价值。es在很多公司应用非常广泛,它已经成为玩大数据的必备的技能,在之前的章节我吐槽过es写方面的问题,今天将吐槽下es查询-terms语法的那些坑,这里探讨两点:一个是多terms并发带来高CPU,另一个是terms使用不当会导致bug。

业务场景

我们基于门店+商品做了一个业务上的大宽表,有各种维度的查询,需要分页,比如按商品的采购组织、采购组、商品的类别(部类-大类-中类-小类)、商品名称查询等

{undefined"from": 0,"size": 200,"timeout": "30s","query": {undefined"bool": {undefined"must": [{undefined"terms": {undefined"purchase_org_code": ["${purchase}"],"boost": 1.0}},{undefined"terms": {undefined"purchase_group_code": ["P05","P06","P07","P08","P09","P10","P11","U01","U02","U03","U04","U05","U06","U07","U08","U09","U10","U11","U12","U13","U15","U16","U17","U18"],"boost": 1.0}},{undefined"terms": {undefined"product_code": ["${product1}","${product2}","${product3}","${product4}","${product5}"],"boost": 1.0}}],"adjust_pure_negative": true,"boost": 1.0}}
}

集群规模

目前集群规模也不大,版本是6.7.1,大概是3个data3个master,这里我展示其中一台data数据存储情况:

doc.count:3.4亿
jvm.heap:12g
store.size:106G
os.men:15G

压测的结果

what?你没看错,TPS不行,失败率太高了。最后通过监控发,IO-wait太乱了,通过命令检测发现,IO读(即使是不是随机读)基本上就是40~50mb的峰值,延迟超5s以上。真是服了拿着高薪玩着云计算的同事,号称云版SSD盘,距离阿里云ECS的SSD差点不是一点点,以下是阿里云上的给出的指标


注:1GBps=1024/8=128MB

下云-建新集群-数据迁移

采用了虚拟机的方式,磁盘IO-wait不是那么严重了,迁移集群又变得很麻烦。由于集群线上不能停机,而reindex又太慢了,那就盘拷贝吧。

要知道:es的频繁的读盘会受pageCache影响,因此堆内存、机器内存都增加。

由于没有关闭索引,部分translog文件会被损坏了(/data/es/nodes/0/indices/jkZF34s-SVuGhTiBgDv2cw/3/translog/translog-185.tlog文件恢复不了),只能把这个索引删掉,老老实实的reindex。

热门问题:99%… cpu usage by thread…6/10 snapshots sharing following 29 elements

继续并发压测的确失败率降下来了,但16核的CPU跑满了!!

注意:第二个查询条件里,每个terms里数据比第一个多了将近30个,差异地区很大的。


看监控也确实是usr进程,通过_nodes/hot_threads便可得到上面的那个热门问题(google都能搜到不下于百万级同样问题的反馈,即使是官方也不少)

"elasticsearch[yhs-cgzt-esdata-prod1][search][T#19]" #171 daemon prio=5 os_prio=0 tid=0x00007f326c041000 nid=0x4c76 runnable [0x00007f15752d6000]java.lang.Thread.State: RUNNABLEat org.apache.lucene.util.BitSet.or(BitSet.java:95)at org.apache.lucene.util.FixedBitSet.or(FixedBitSet.java:271)at org.apache.lucene.util.DocIdSetBuilder.add(DocIdSetBuilder.java:151)at org.apache.lucene.search.TermInSetQuery$1.rewrite(TermInSetQuery.java:259)at org.apache.lucene.search.TermInSetQuery$1.scorer(TermInSetQuery.java:322)at org.apache.lucene.search.Weight.scorerSupplier(Weight.java:143)at org.apache.lucene.search.LRUQueryCache$CachingWrapperWeight.scorerSupplier(LRUQueryCache.java:719)at org.elasticsearch.indices.IndicesQueryCache$CachingWeightWrapper.scorerSupplier(IndicesQueryCache.java:157)at org.apache.lucene.search.BooleanWeight.scorerSupplier(BooleanWeight.java:364)at org.apache.lucene.search.BooleanWeight.scorer(BooleanWeight.java:330)at org.apache.lucene.search.Weight.bulkScorer(Weight.java:177)at org.apache.lucene.search.BooleanWeight.bulkScorer(BooleanWeight.java:324)at org.elasticsearch.search.internal.ContextIndexSearcher$1.bulkScorer(ContextIndexSearcher.java:180)at org.apache.lucene.search.IndexSearcher.search(IndexSearcher.java:667)at org.elasticsearch.search.internal.ContextIndexSearcher.search(ContextIndexSearcher.java:191)at org.apache.lucene.search.IndexSearcher.search(IndexSearcher.java:471)at org.elasticsearch.search.query.QueryPhase.execute(QueryPhase.java:276)at org.elasticsearch.search.query.QueryPhase.execute(QueryPhase.java:114)at org.elasticsearch.search.SearchService.loadOrExecuteQueryPhase(SearchService.java:349)at org.elasticsearch.search.SearchService.executeQueryPhase(SearchService.java:393)at org.elasticsearch.search.SearchService.access$100(SearchService.java:125)at org.elasticsearch.search.SearchService$2.onResponse(SearchService.java:358)at org.elasticsearch.search.SearchService$2.onResponse(SearchService.java:354)at org.elasticsearch.search.SearchService$4.doRun(SearchService.java:1085)at org.elasticsearch.common.util.concurrent.AbstractRunnable.run(AbstractRunnable.java:37)at org.elasticsearch.common.util.concurrent.TimedRunnable.doRun(TimedRunnable.java:41)at org.elasticsearch.common.util.concurrent.ThreadContext$ContextPreservingAbstractRunnable.doRun(ThreadContext.java:751)at org.elasticsearch.common.util.concurrent.AbstractRunnable.run(AbstractRunnable.java:37)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)at java.lang.Thread.run(Thread.java:745)

堆栈看上去没有啥问题,这个问题一般而言(低成本),再准备一个es集群(选什么版本呢,你认为最稳定bug,es更新太频繁了:笔者经验及社区里反馈不止一次遇到过版本问题,每个小版本更新的bug非常多),使用同样的数据同样的配置再试下。

不好意思,很不幸,问题依然还是那个问题,这时恭喜你摊上事情了,只能硬啃了。

首先针对GC的问题(频繁的GC,GC一次时间很长),按某大牛建议,需安装Java 11(他说es7.1以上版本,自带java11),切换成G1,也确实问题得到了解决。

G1GC Configuration

NOTE: G1GC is only supported on JDK version 10 or later.

To use G1GC uncomment the lines below.

-XX:-UseConcMarkSweepGC
-XX:-UseCMSInitiatingOccupancyOnly
-XX:+UseG1GC
-XX:InitiatingHeapOccupancyPercent=75

其实如果你有足够的玩ES经验,从hot_threads中snapshots sharing following 29 elements 可以推测这涉及到大量的查询,很不正常。对,借助监控api就足以验证这点(Query Count和Fetch Count确实很高啊)

关于Query和Fetch 补充点背景知识ES的查询和mapreduce也很类似,首先ES客户端会将这个搜索词同时向5个分片发起搜索请求,他们基于本Shard独立完成搜索,这叫Scatter,然后将符合条件的结果全部返回这一步叫Gather。数量问题当用户搜索某个词需要返回最符合条件的前10条,但在5个分片中每个分片都会返回符合条件的10条记录,此时,客户端最多会收到10*5=50条记录。排名问题每个分片计算分值(包括词频率)都是基于自己的分片数据进行计算的,而ES进行整体排名是基于每个分片计算后的分值进行排序的,这就可能会导致排名不准确的问题。这就可能会导致排名不准确的问题。如果我们想更精确的控制排序,应该先将计算排序和排名相关的信息(词频率等)从5个分片收集上来,进行统一计算,然后使用整体的词频率去每个分片进行查询。ES对数量问题和排名问题也没有什么较好的解决方法,最终把选择的权利交给用户。es在查询时可以指定搜索类型为:QUERY_THEN_FETCH(默认),大概分两个步骤,第一步,先向所有的shard发出请求,各分片只返回排序和排名相关的信息(注意,不包括文档document),然后按照各分片返回的分数进行重新排序和排名,取前size个文档。然后进行第二步,去相关的shard取document。这种方式返回的document与用户要求的size是相等的。优点是数据量是准确的,但性能一般并且数据排名不准确。
QUERY_AND_FEATCH,向索引的所有分片(shard)都发出查询请求,各分片返回的时候把元素文档(document)和计算后的排名信息一起返回。这种搜索方式是最快的。这种查询方法只需要去shard查询一次。但是各个shard返回的结果的数量之和可能是用户要求的size的n倍。优点是搜索方式是最快的,但返回的数据量不准确。
DFS_QUERY_THEN_FEATC,这种方式比第一种方式多了一个初始化散发(initial scatter)步骤,先对所有分片发送请求, 把所有分片中的词频和文档频率等打分依据全部汇总到一块, 再执行后面的操作。优点很明显,数据量是准确并且排名也准确,但性能是最差的。
DFS_QUERY_AND_FEATCH,这种方式比第二种多了一个初始化散发(initial scatter)步骤,可以更精确控制搜索打分和排名,过程与上一种类似,优点是排名准确,但返回的数据量不准确,可能返回(N*分片数量)的数据。
从性能考虑QUERY_AND_FETCH是最快的且性能最好,DFS_QUERY_THEN_FETCH是最慢的、性能一般、数据排名也不准确。从搜索的准确度来说,DFS要比非DFS的准确度更高。可参阅6.2系列文档

多个terms搜索

在mysql中的方言中有这样的语法:select * from user where (user_id,type) in ((568,6),(569,6),(600,8)),此时使用terms很显然是无能无力的(包括笔者也犯过这样低级的问题),它查询的逻辑更多笛卡尔乘积的并集。

注:交集要有预估,如果terms的个数较多并且terms中的词数目也比较多,多个大数据集合并,很容易占核,稍微一点并非就会game over。

确实业务诉求也要满足,笔者想到了2中对策:

index时,需查询的多列合并为一列,就不存在数据交叉的问题
使用bool多层嵌套,满足语义

{undefined"from": 0,"size": 200,"timeout": "30s","query": {undefined"bool": {undefined"should": [{undefined"bool": {undefined"must": [{undefined"term": {undefined"shop_code": "9120"}},{undefined"term": {undefined"product_code": "771630106"}}]}},{undefined"bool": {undefined"must": [{undefined"term": {undefined"shop_code": "9011"}},{undefined"term": {undefined"product_code": "771630106"}}]}}]}}
}

es确实很强健,超过1024个term,就报错了…无语…

如果用terms查询,传入数组,默认最大长度65535,也可以通过index.max_terms_count更改这个值。

的确,es默认支持元素数量为1024个(可调整配置elasticsearch.yml的index.query.bool.max_clause_count: 10240)

回去看了代码

目前es 7.3.1 仅仅支持这2 种,使用大多数 使用DFS_QUERY_THEN_FETCH

研究探讨elasticsearch 高cpu问题相关推荐

  1. 再谈elasticsearch 高cpu问题

    首先要阐述一个观点,任何技术都是为解决某一个领域的问题而存在的,我们在使用它的时候,尽可能使用它的优势(亮点),去发挥它应具备的业务价值.es在很多公司应用非常广泛,它已经成为玩大数据的必备的技能,在 ...

  2. M1卡破解(智能卡攻防技术分层、分级研究探讨)

    本来是当任务来完成的一篇论文,原计划有些宏伟,迫于时间太紧根本无法完成它,就草草成了现在这个样子交差了.交出去如石沉大海,了无消息.干脆发到这里也不辜负我加班写作的辛苦了. 许多材料来不及验证,可能有 ...

  3. java udp 线程,Java中的UDP DatagramSocket线程的高CPU使用率

    我正在运行一个多线程java服务器应用程序,其中包括在3个不同的线程上从3个不同的组播源(端口)接收UDP数据包 . 它运行在最近的双插槽redhat机箱上(总共8个核心(4 x 2 cpu),没有超 ...

  4. 网站制作流程及界面交互设计研究探讨

    很多朋友希望,我能把我做网站的一些流程及经验跟大家分享一下,最近刚好做一次内部培训,所以稍微整理了一下,这些只是针对网页初学者,具有一定平面设计水平的人,对HTML不是很了解,这里有很多都是一些我个人 ...

  5. elasticsearch的CPU居高不下的问题

    最近项目中遇到一个令人头疼的问题,毕竟因为工作需要刚学elasticsearch,也没有去关注elasticsearch的配置问题,安装好默认把它当做数据库一样去使用,这导致接下来的项目直接挂掉... ...

  6. ELK之elasticsearch导致CPU居高不下系统慢解决办法

    参考:http://zoufeng.net/2018/07/16/cpu-of-elasticsearch-high-search-slow/ elasticsearch主机CPU居高不下100%左右 ...

  7. 生物医学大数据处理研究探讨

    生物医学大数据处理研究探讨 摘要: 随着生物分析和计算技术的快速发展以及医疗信息化水平的不断提高, 生物医学领域 产生了大量的数据,促进了生物医学大数据的形成,也使得生物医学的研究由原来的假设 驱动向 ...

  8. mysql占用cpu_Mysql占用过高CPU时的优化手段(必看)

    Mysql占用CPU过高的时候,该从哪些方面下手进行优化? 占用CPU过高,可以做如下考虑: 1)一般来讲,排除高并发的因素,还是要找到导致你CPU过高的哪几条在执行的SQL,show process ...

  9. 高cpu_实用脚本:检查高 CPU / 内存消耗进程 | Linux 中国

    本教程中包含两个脚本,它们可以帮助你确定 Linux 上高 CPU/内存消耗进程的运行时间. • 来源:linux.cn • 作者:Magesh Maruthamuthu • 译者:geekpi • ...

最新文章

  1. Lab模式的妙用--人像处理
  2. 手把手教你使用zabbix监控nginx
  3. 数据库事务的悲观锁和乐观锁
  4. 02繁花嗅Django笔记
  5. 网络知识:为什么你家里的网速慢,看完你就懂了?
  6. Go语言重新开始,Go Modules 的前世今生与基本使用
  7. 190628 - 解决新版本LastPass没有谷歌套件时打开就闪退的问题.md
  8. java程序知识_java的基本知识点
  9. ** 安装好的Apache服务器不能在本地计算机启动
  10. 帆软超级链接对象、插入子报表、网页框传参的个人使用心得
  11. linux的使用 --- 安装git
  12. 《通用规范汉字表》2013版
  13. 使用虚拟机镜像文件导入部署openGauss
  14. Django实战: Python爬虫爬取链家上海二手房信息,存入数据库并在前端显示
  15. 拼音加加 4.0 正式版发布了
  16. 基于Excel的实验室设备管理系统设计
  17. Linux上 如何查找yum安装包所缺缺少的依赖包及报错处理
  18. JavaScript js 实现拖动窗口移动功能
  19. 一文带你搞定svg-icon的使用
  20. php怎么显示gif图片,如何让伪进度条在页面上显示成gif图像

热门文章

  1. 如何写好科研论文 撰写技巧(一)
  2. windows上传ipa到开发者中心,上架app到苹果应用市场
  3. bui ajax,BUI 数据交互
  4. bui java,前端BUI框架表单教程
  5. Node.js之Stream
  6. linux查看进程命令wwn,linux下如何查看服务器wwn号
  7. 任意进制到十进制的转换
  8. 超8成项目存在高危开源漏洞 《2021中国软件供应链安全分析报告》发布
  9. 用英语婉转的拒绝表白...
  10. 《论文阅读》Bidirectional LSTM-CRF Models for Sequence Tagging