问题: 慢查问
搜寻平台的公共集群,因为业务泛滥,对业务的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": \[\]
}
}
这个查问比较简单,翻译一下就是:
SELECT guid FROM xxx WHERE source=5 AND type=21 AND creator='0d754a8af3104e978c95eb955f6331be' AND status in (0,3) AND isDeleted=0;
慢查问剖析
这个查问问题还挺多的,不过不是明天的重点。比方这外面不好的一点是还用了含糊查问fuzzy_transpositions,也就是查问ab的时候,ba也会被命中,其中的语法不是明天的重点,能够自行查问,我预计这个是业务用了SDK主动生成的,外面很多都是默认值。
第一反馈是当然是用filter来代替match查问,一来filter能够缓存,另外防止这种无意义的含糊匹配查问,然而这个优化是无限的,并不是明天解说的关键点,先疏忽。
错用的数据类型
咱们通过kibana的profile来进行剖析,耗时到底在什么中央?es有一点就是开源社区很沉闷,文档齐全,配套的工具也十分的不便和齐全。
能够看到大部分的工夫都花在了PointRangQuery外面去了,这个是什么查问呢?为什么这么耗时呢?这里就波及到一个es的知识点,那就是对于integer这种数字类型的解决。在es2.x的时代,所有的数字都是按keyword解决的,每个数字都会建一个倒排索引,这样查问尽管快了,然而一旦做范畴查问的时候。比方 type>1 and type<5就须要转成 type in (1,2,3,4,5)来进行,大大的减少了范畴查问的难度和耗时。
之后es做了一个优化,在integer的时候设计了一种相似于b-tree的数据结构,减速范畴的查问,具体能够参考(https://elasticsearch.cn/arti…)
所以在这之后,所有的integer查问都会被转成范畴查问,这就导致了下面看到的isDeleted的查问的解释。那么为什么范畴查问在咱们这个场景下,就这么慢呢?能不能优化。
明明咱们这个场景是不须要走范畴查问的,因为如果走倒排索引查问就是O(1)的工夫复杂度,将大大晋升查问效率。因为业务在创立索引的时候,isDeleted这种字段建成了Integer类型,导致最初走了范畴查问,那么只须要咱们将isDeleted类型改成keyword走term查问,就能用上倒排索引了。
实际上这里还波及到了es的一个查问优化。相似于isDeleted这种字段,毫无区分度的倒排索引的时候,在查问的时候,es是怎么优化的呢?
多个Term查问的程序问题
实际上,如果有多个term查问并列的时候,他的执行程序,既不是你查问的时候,写进去的程序。
例如下面这个查问,他既不是先执行source=5再执行type=21依照你代码的程序执行过滤,也不是同时并发执行所有的过滤条件,而后再取交加。es很聪慧,他会评估每个filter的条件的区分度,把高区分度的filter先执行,以此能够减速前面的filter循环速度。比方creator=0d754a8af3104e978c95eb955f6331be查出来之后10条记录,他就会优先执行这一条。
怎么做到的呢?其实也很简略,term建的时候,每一个term在写入的时候都会记录一个词频,也就是这个term在全副文档里呈现的次数,这样咱们就能判断以后的这个term他的区分度高下了。
为什么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更有益处。
(本文作者:任天兵)
本文系哈啰技术团队出品,未经许可,不得进行商业性转载或者应用。非商业目标转载或应用本文内容,敬请注明“内容转载自哈啰技术团队”。
发表回复