ES搜索性能优化
在项目中有用到Elastic Search来进行数据的索引和搜索工作。在一开始接手的时候,搜索接口的耗时在六七百毫秒,相对一般的接口来说是一个比较耗时的操作了。
在后期的优化和学习过程中,对ES的特性和原理进行初步的了解,并对ES的搜索性能进行了优化,将搜索接口的耗时降低到了平均150ms左右,性能提高了将近5倍。
虽然是个很小的改动,但效果确实显著,记录一下中间具体的过程。
目录
初始ES搜索的概况
在一开始的ES搜索中,其实也是一个比较简单的过滤条件。隐藏一些业务细节,最抽象的版本可以概括为
{
"query": {
"function_score": {
"query": {
"bool": {
"filter": [
{
"terms": {
"status": [1,2]
}
},
.....
]
}
.....
},
"functions": [
.....
],
"score_mode": "sum",
"boost_mode": "sum"
}
},
"from": 0,
"size": 20
}
在用kibina自带的非常方便的工具来分析具体的搜索过程耗时时,发现最主要的时间花在了build_scorer的过程中。如果把build_scorer的耗时减少,能够大幅的加快接口的处理速度。
在不断的试验过程中,发现如果将status的过滤条件删除,耗时大为减少,所以目标确定为找到status的过滤条件耗时长的原因。
经过各方面的资料查找,发现网上的很多建议并不靠谱,或者说并不适合我们所面临的状态。不过幸好,最终发现了这篇文章: https://www.jianshu.com/p/e9be6740b724 , 一下子解决了我们的问题。这篇文章对优化的原理讲的很明白了, 这边只是个人再记录一下。
ES优化建议
直接说结论: 对于数值类型,需要在构建索引的时候就明确这个字段是否需要进行范围查询RangeQuery,还是只需要做精确的TermQuery;如果类似我们的这种status字段,只需要进行精确的TermQuery,或者查询少数的几种数值情况,在构建索引的时候,需要预先指定对应的字段为keyword类型,而不要使用ES默认的数值类型short。
ES优化原理
再记录一下上述优化建议的原理。
在ES5.x之前用到的Lucene版本,即使是被定义成数值类型,最终其实还是按照文本类型对其进行了倒排索引。
所以针对上边我们的情况,如果在ES5.x之前的版本,status字段即使是存成了默认的数值字段,还是会在底层建立倒排索引,上边的搜索条件其实也是会非常快的。
这种处理方式虽然对这种TermQuery的搜索比较快速,但是如果要用到RangeQuery,就有点力不从心了。 对于RangeQuery,比如status range from 0 to 100,这种情况就需要查询 status 从0到100的倒排索引,尤其在RangeQuery的范围比较大的时候,性能缺陷更为明显。
为了加快RangeQuery,ES5.x用到的Lucene对数值类型进行了优化,将其从倒排索引转成了Block k-d tree的数据结构。Block k-d tree可以简单的理解成为一个二叉树的结构,一方面采用了这种类似二叉树的结构,另一方面ES针对RangeQuery进行了一系列的优化,加快了RangeQuery的搜索效率。
正是由于ES5.x针对数值类型进行了这方面的优化,虽然对于RangeQuery更有效率,但是对于TermQuery的情况,由于没有了倒排索引,所以在进行TermQuery的时候,需要先根据符合条件的docid构建出一个巨大的bitset出来。所以增加了耗时。
所以结论就是,如果要进行TermQuery,索引的类型适合定义成keyword类型,以利用它的倒排索引结构; ES的默认的数值类型更适合进行RangeQuery的查询。