Elasticsearch长文本查询拒绝问题分析及性能优化

2021-06-21 20:46:54 浏览数 (1)

问题背景:

腾讯云ES客户-某头部在线教育公司在微信群中反馈连续两天在晚上19:30左右业务侧查询ES集群时出现较大面积查询拒绝现象,且查询耗时从原先的100ms以下上涨到900ms以上,如图1所示。

图1. 客户业务端统计的查询耗时监控图1. 客户业务端统计的查询耗时监控

排查过程:

收到客户反馈后第一时间介入排查,首先查看集群日志,确实出现了大量的查询队列堆积和查询拒绝的现象,如图2所示。

图2. 集群查询拒绝日志图2. 集群查询拒绝日志

并且从监控中可以看出一个非常明显的规律:从16:00左右开始(学生放学时间?),cpu利用率随着查询流量的上涨不停升高,一直飙升到90%以上,开始出现查询队列堆积和查询拒绝,如图3、4所示。

图3. 集群单周期查询完成次数监控(次/分)图3. 集群单周期查询完成次数监控(次/分)
图4. 集群最大cpu使用率监控图4. 集群最大cpu使用率监控

从上面的cpu和查询流量监控趋势图中我们大致判断客户反馈的查询拒绝现象和cpu利用率飙升到90%以上有关,而cpu的暴涨又是由于查询流量的逐步攀升导致,而cpu被打满又反向导致查询请求处理缓慢,加剧了队列的堆积,形成恶性循环。为了验证自己的想法,抓取了当时的热线程,从热线程的火焰图中证明了自己的猜想。

代码语言:javascript复制
GET /_nodes/hot_threads  // 获取集群节点热线程信息
图5. hot_threads火焰图图5. hot_threads火焰图

确认了查询拒绝产生的根因后本以为是集群配置较低导致的集群性能瓶颈,查看了集群的配置后立马放弃让客户扩容的想法,客户的集群配置如下:

代码语言:javascript复制
集群配置:(32C128G   SSD) * 8节点

查询超时的索引index-zuoye(化名)主分片设置的是16主1副本,总doc数1亿 。分析了该索引的settings和mapping设置,发现有一个字段是text类型。

图6. 索引mapping中text字段图6. 索引mapping中text字段

随后从该索引中随机抽查了几条数据,可以看出每条doc中该字段的文本都非常长,多达上百字,可见该字段存储的是作业的题目。

图7. text字段中存储的内容图7. text字段中存储的内容

而从集群慢日志中捞出的查询语句中可以看出,客户查询的DSL中有对该长文本字段的模糊匹配查询。

代码语言:javascript复制
{"from":0,"size":30,"query":{"bool":{"must":[{"match":{"xxxcontent":{"query":" sinana (a 02e) 列8题,16分) A C D ①小区要在综裂找放口$上图标指导居民按类权放垃叔,请选图上连图标中最适食作为 旋口的一幅,并熨要设用理由,(2分) ②相据上图,说液下列比圾应如何分类?为什么?(2分) 反验子线两灯纸反过本块 ③阅读下面材料,说说这级分类的好处有哪些。(2分) 材料:号就计,我国每年产生的且圾总量约有10亿吃,并还在以每年5分一89的选度对长与此 是严重不足的减市比风处理能力,中国首有正分之二的城率面沿"红叔周城"的建境 复说我国主要约柱红收集方式是混合礼集,混合收集存在着:用加且圾无客化处度约 有用物度的纯度和开利用价值:过加了为处理垃以(如地把)而做的后棱分热工作等问题、因元,提 生活址叔的分类收条是必要且相当有意义的","operator":"OR","analyzer":"ik_smart","prefix_length":0,"max_expansions":50,"fuzzy_transpositions":true,"lenient":false,"zero_terms_query":"NONE","auto_generate_synonyms_phrase_query":true,"boost":1.0}}},{"match":{"product_xx":{"query":1,"operator":"OR","prefix_length":0,"max_expansions":50,"fuzzy_transpositions":true,"lenient":false,"zero_terms_query":"NONE","auto_generate_synonyms_phrase_query":true,"boost":1.0}}}],"filter":[{"range":{"createtime":{"from":1623927599,"to":1623945599,"include_lower":true,"include_upper":true,"boost":1.0}}}],"adjust_pure_negative":true,"boost":1.0}}}

我们知道ES在对text类型做模糊查询前首先会对该字段的文本进行分词,然后对分词后的token进行匹配,而在高并发场景下对这种动则几百个字的文本进行分词和match无疑是相当消耗cpu性能的。因此我们逐步理清了集群每晚19:30左右查询拒绝的深层次原因:该索引存储的是作业的题目,学生每天放学后写作业会通过手机扫描题目进行全文本匹配,随着查询流量的上涨,cpu利用率不断攀升,cpu暴涨到90%以上导致查询请求处理不过来从而导致查询队列堆积,查询队列堆积导致新进来的请求被拒绝。而cpu暴涨的根本原因则在于查询请求中对超长文本的作业题目做大量的分词和匹配上。

为了分析查询时间主要消耗在哪里,在查询请求体中加上profile参数,从返回信息中可以看出有上百个子查询且对文本分词分的非常细如图7所示,从而导致基本是对所有的doc进行大量的重复匹配,如图8所示。

图8. 长文本分词信息图8. 长文本分词信息

从之前的热线程火焰图中也可以看出,大部分的查询时间主要开销在倒排链的匹配上,如图9所示。

图9. 热线程火焰图时间开销图9. 热线程火焰图时间开销

优化建议

我们通过前面对集群日志、监控等指标的深入分析和排查,最终发现业务员高峰期查询拒绝的主要原因在于长文本模糊匹配上。因此针对该场景我们给客户提出了如下三点建议:

1、更新IK停用词

通过我们从客户的索引中随机抽查了近300条文档来看,发现大部分文档(作业题目)中都包含了一些共性的但对搜索没有太大价值的词语,如“题目,下图,得分,如图,如图所示,填空,审题等”,这些词同样会在查询时参与分词,但是由于在大部分题目中都有出现,因此区分度不大,还会导致倒排链非常长。因此为了降低分词时cpu的性能损耗及在匹配时的时间开销,建议客户将这些高频的、分区度不大的词语添加到IK停用词字典中,让其在查询时不参与分词和搜索。

为了证明自己的建议,我在争得客户同意的前提下,在腾讯云ES控制台购买了一个配置和客户一样的集群,并使用客户的索引进行测试。测试结果如表1所示:

表1. ES集群更新停用词前后测试性能对比

查询并发流量

cpu最大利用率

平均took

更新IK停用词前

10000 /s

92%

220ms

更新IK停用词后

10000 /s

85%

139ms

因此从我们测试集群的压测结果来看,增加停用词词典后,查询的性能几乎可以达到近100%的性能提升。这里需要说明的是最终的停用词词典还需要业务进一步测试和确认,因为过多的添加停用词有可能会导致查询结果的失真。

2、题库按学科分类

我们知道搜索性能的优化一定是需要结合具体的业务场景来进行的。而从客户当前的业务场景来看,每一次搜题会对整个题库进行全文本匹配,对查询性能会有一定的影响。考虑到作业题目天然具有学科属性,因此我们建议给索引增加学科字段,每条doc按学科进行分类。并在查询语句中通过filter进行学科过滤,可以缩小搜索范围,提高查询性能。

3、增加主分片个数

这条建议可能会让很多ES用户感觉到困惑,为啥不是降主分片而是加主分片,官方建议对于索引文档数较小在千万级的,主分片个数最好设置为1个。我们之前也配合过国内某头部生鲜O2O客户做过深度聚合查询性能压测,文档数小于1亿时,索引设置1个主分片相对于3个主分片,深度聚合的查询性能要高出20%左右,见图10所示。

图10. 某生鲜O2O客户深度聚合查询性能监控图10. 某生鲜O2O客户深度聚合查询性能监控

而我们建议客户将主分片从原来的16调整到32的原因在于,客户的查询都是超长文本的模糊匹配,且分词非常细。这就导致这种查询,数据量不大,本质上会对每条doc 都要进行大量的query 匹配,因此每个分片的数据量越少,某些倒排链越短,并发越高,处理得反而会越快。该建议的提出我们也是有非常充分的测试数据做支撑的。我们在测试集群上分别reindex了三个索引,分别是V2 16主1副本,V3 4主2副本, V4 32主1副本,如图11所示。

图11. 测试集群不同索引主分片设置图11. 测试集群不同索引主分片设置

经过反复的压测,得到的数据如表2所示:

表2. 索引设置不同主分片测试性能对比

索引设置

查询并发流量

最大cpu使用率

平均took

V2 16主 1副本

10000 /s

92%

220%

V3 4主 2副本

10000 /s

94%

800%

V4 32主 1副本

10000 /s

87%

190ms

做完了如上几条优化工作之后,可以看到cpu的最大利用率在高峰期也一直保持在50%以上,如图12所示,效果较为明显。且未再出现过查询拒绝的问题,如图13所示。

图12. 客户集群cpu最大利用率近两周监控图12. 客户集群cpu最大利用率近两周监控
图13. 客户集群查询拒绝率近两周监控图13. 客户集群查询拒绝率近两周监控

总结

对较长文本的模糊查询是一种较为消耗cpu资源的操作,在高并发场景下更容易导致cpu先达到性能瓶颈,从而导致查询队列的堆积、拒绝和超时。我们通常可以通过结合具体的业务场景来分析,从而给出一些优化建议,比如从业务查询类型来调整分片数的设置、多设置一些过滤条件,通过filter来提升查询性能等。希望本文能够给大家一些启发。

0 人点赞