您的当前位置:首页 >数据库 >两小时 Elasticsearch 性能优化,直接把慢查询干团灭了…… 正文
时间:2025-11-04 21:00:55 来源:网络整理编辑:数据库
问题:慢查询搜索平台的公共集群,由于业务众多,对业务的es查询语法缺少约束,导致问题频发。业务可能写了一个巨大的查询直接把集群打挂掉,但是我们平台人力投入有限,也不可能一条条去审核业务的es查询语法
搜索平台的优化公共集群,由于业务众多,直接对业务的把慢es查询语法缺少约束,导致问题频发。查询业务可能写了一个巨大的干团查询直接把集群打挂掉,但是优化我们平台人力投入有限,也不可能一条条去审核业务的直接es查询语法,只能通过后置的把慢手段去保证整个集群的稳定性,通过slowlog分析等,查询下图中cpu已经100%了。干团

昨天刚好手头有一点点时间,优化就想着能不能针对这些情况,直接把影响最坏的把慢业务抓出来,进行一些改善,查询于是干团昨天花了2小时分析了一下,找到了一些共性的问题,可以通过平台来很好的改善这些情况。
首先通过slowlog抓到一些耗时比较长的查询,例如下面这个索引的查询耗时基本都在300ms以上:
复制
{
"from": 0,
"size": 200,
"timeout": "60s",
"query": {
"bool": {
"must": \[
{
"match": {
"source": {
"query": "5",
"operator": "OR",
"prefix_length": 0,
"fuzzy_transpositions": true,
"lenient": false,
"zero_terms_query": "NONE",
"auto_generate_synonyms_phrase_query": "false",
"boost": 1}
}
},
{
"terms": {
"type": [
"21"],
"boost": 1}
},
{
"match": {
"creator": {
"query": "0d754a8af3104e978c95eb955f6331be",
"operator": "OR",
"prefix_length": 0,
"fuzzy_transpositions": "true",
"lenient": false,
"zero_terms_query": "NONE",
"auto_generate_synonyms_phrase_query": "false",
"boost": 1}
}
},
{
"terms": {
"status": [
"0",
"3"],
"boost": 1}
},
{
"match": {
"isDeleted": {
"query": "0",
"operator": "OR",
"prefix_length": 0,
"fuzzy_transpositions": "true",
"lenient": false,
"zero_terms_query": "NONE",
"auto_generate_synonyms_phrase_query": "false",
"boost": 1}
}
}
\],
"adjust\_pure\_negative": true,
"boost": 1}
},
"\_source": {
"includes": [
],
"excludes": []
}
}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.64.65.66.67.68.69.70.71.72.73.74.75.76.77.78.这个查询比较简单,翻译一下就是:
复制SELECT guid FROM xxx WHERE source=5 AND type=21 AND creator=0d754a8af3104e978c95eb955f6331be AND status in (0,3) AND isDeleted=0;1.这个查询问题还挺多的免费信息发布网,不过不是今天的重点。比如这里面不好的一点是还用了模糊查询fuzzy_transpositions,也就是查询ab的时候,ba也会被命中,其中的语法不是今天的重点,可以自行查询,我估计这个是业务用了SDK自动生成的,里面很多都是默认值。
第一反应当然是用filter来代替match查询,一来filter可以缓存,另外避免这种无意义的模糊匹配查询,但是这个优化是有限的,并不是今天讲解的关键点,先忽略。源码库
1、错用的数据类型我们通过kibana的profile来进行分析,耗时到底在什么地方?es有一点就是开源社区很活跃,文档齐全,配套的工具也非常的方便和齐全。

可以看到大部分的时间都花在了PointRangQuery里面去了,这个是什么查询呢?为什么这么耗时呢?这里就涉及到一个es的知识点,那就是对于integer这种数字类型的处理。在es2.x的时代,所有的数字都是按keyword处理的,每个数字都会建一个倒排索引,这样查询虽然快了,但是一旦做范围查询的时候。比如 type>1 and type<5就需要转成 type in (1,2,3,4,5)来进行,大大的增加了范围查询的难度和耗时。b2b信息网
之后es做了一个优化,在integer的时候设计了一种类似于b-tree的数据结构,加速范围的查询,详细可以参考(https://elasticsearch.cn/article/446)
所以在这之后,所有的integer查询都会被转成范围查询,这就导致了上面看到的isDeleted的查询的解释。那么为什么范围查询在我们这个场景下,就这么慢呢?能不能优化。
明明我们这个场景是不需要走范围查询的,因为如果走倒排索引查询就是O(1)的时间复杂度,将大大提升查询效率。由于业务在创建索引的时候,isDeleted这种字段建成了Integer类型,导致最后走了范围查询,那么只需要我们将isDeleted类型改成keyword走term查询,就能用上倒排索引了。
实际上这里还涉及到了es的一个查询优化。类似于isDeleted这种字段,毫无区分度的倒排索引的时候,在查询的时候,es是怎么优化的呢?
2、多个Term查询的顺序问题实际上,如果有多个term查询并列的时候,他的执行顺序,既不是你查询的时候,写进去的顺序。

例如上面这个查询,他既不是先执行source=5再执行type=21按照你代码的顺序执行过滤,也不是同时并发执行所有的过滤条件,然后再取交集。es很聪明,他会评估每个filter的条件的区分度,把高区分度的filter先执行,以此可以加速后面的filter循环速度。比如creator=0d754a8af3104e978c95eb955f6331be查出来之后10条记录,他就会优先执行这一条。
怎么做到的呢?其实也很简单,term建的时候,每一个term在写入的时候都会记录一个词频,也就是这个term在全部文档里出现的次数,这样我们就能判断当前的这个term他的区分度高低了。
3、为什么PointRangeQuery在这个场景下非常慢上面提到了这种查询的数据结构类似于b-tree,他在做范围查询的时候,非常有优势,Lucene将这颗B-tree的非叶子结点部分放在内存里,而叶子结点紧紧相邻存放在磁盘上。当作range查询的时候,内存里的B-tree可以帮助快速定位到满足查询条件的叶子结点块在磁盘上的位置,之后对叶子结点块的读取几乎都是顺序的。

总结就是这种结构适合范围查询,且磁盘的读取是顺序读取的。但是在我们这种场景之下,term查询可就麻烦了,数值型字段的TermQuery被转换为了PointRangeQuery。这个Query利用Block k-d tree进行范围查找速度非常快,但是满足查询条件的docid集合在磁盘上并非向Postlings list那样按照docid顺序存放,也就无法实现postings list上借助跳表做蛙跳的操作。
要实现对docid集合的快速advance操作,只能将docid集合拿出来,做一些再处理。这个处理过程在org.apache.lucene.search.PointRangeQuery#createWeight这个方法里可以读取到。这里就不贴冗长的代码了,主要逻辑就是在创建scorer对象的时候,顺带先将满足查询条件的docid都选出来,然后构造成一个代表docid集合的bitset,这个过程和构造Query cache的过程非常类似。之后advance操作,就是在这个bitset上完成的。所有的耗时都在构建bitset上,因此可以看到耗时主要在build_scorer上了。
找到原因之后,就可以开始验证了。将原来的integer类型全部改成keyword类型,如果业务真的有用到范围查询,应该会报错。通过搜索平台的平台直接修改配置,修改完成之后,重建索引就生效了。

索引切换之后的效果也非常的明显,通过kibana的profile分析可以看到,之前需要接近100ms的PointRangQuery现在走倒排索引,只需要0.5ms的时间。

之前这个索引的平均latency在100ms+,这个是es分片处理的耗时,从搜索行为开始,到搜索行为结束的打点,不包含网络传输时间和连接建立时间,单纯的分片内的函数的处理时间的平均值,正常情况在10ms左右。

经过调整之后的耗时降到了10ms内。

通过监控查看慢查询的数量,立即减少到了0。

后续将通过搜索平台侧的能力来保证业务的查询,所有的integer我们会默认你记录的是状态值,不需要进行范围查询,默认将会修改为keyword类型,如果业务确实需要范围查询,则可以通过后台再修改回integer类型,这样可以保证在业务不了解es机制的情况下,也能拥有较好的性能,节省机器计算资源。
目前还遇到了很多问题需要优化。例如重建索引的时候,机器负载太高。公共集群的机器负载分布不均衡的问题,业务的查询和流量不可控等各种各样的问题,要节省机器资源就一定会面对这种各种各样的问题,除非土豪式做法,每个业务都拥有自己的机器资源,这里面有很多很多颇具技术挑战的事情。
实际上,在这一块还是非常利于积累经验,对于es的了解和成长也非常快,在查问题的过程中,对于搜索引擎的使用和了解会成长的非常快。不仅如此,很多时候,我们用心地看到生产的问题,持续的跟踪,一定会有所收获。大家遇到生产问题的时候,务必不要放过任何细节,这个就是你收获的时候,比你写100行的CRUD更有好处。
Mac装系统教程(从准备工作到系统安装,全方位指导助你顺利完成)2025-11-04 20:02
一文告诉你全世界头部的开发者都在使用什么数据库2025-11-04 19:58
详解Oracle中三大类型与隐式数据类型转换2025-11-04 19:54
如何使用Ketshash检测可疑的特权NTLM连接2025-11-04 19:49
电脑报read错误的解决方法(遇到电脑报read错误如何应对,尝试这些方法解决问题)2025-11-04 19:35
从《2023年电力安全监管重点任务》看五项安全技术的应用2025-11-04 19:16
分享两个实用sql:根据会话查询当前和历史执行过的sql2025-11-04 19:03
Redis集群模式搭建与原理详解2025-11-04 19:02
腾讯会议电脑使用技巧教程(掌握腾讯会议,提升工作效率的关键技巧)2025-11-04 18:37
黑客创建恶意Dota 2游戏模式,秘密部署后门2025-11-04 18:27
用大白菜启动系统的方法与步骤(快速了解大白菜启动系统并实施启动)2025-11-04 20:52
这个MySQL8.0.16新特性,你知道吗2025-11-04 20:43
MySQL导入导出命令-mysqldump2025-11-04 20:39
当同事用Root权限输入rm -rf 后,鬼知道我经历了什么2025-11-04 19:58
键盘之错(纠正错误,展开思维)2025-11-04 19:55
2023年的网络安全应对措施和优秀实践将如何进展2025-11-04 19:37
黑客可以监听用户对话!谷歌 Home 音箱出现安全问题2025-11-04 19:12
如何使用Decodify递归检测和解码编码字符串2025-11-04 19:02
电脑迷你世界注册教程(轻松注册,畅玩电脑迷你世界)2025-11-04 18:49
2023年值得关注的XDR解决方案(国外版)2025-11-04 18:18