Elasticsearch 5.x 源码分析(12)对类似枚举数据的搜索异常慢的一种猜测

2017-1-9 更新:
https://elasticsearch.cn/article/446
why this happened? 携程的兄弟给出了答案,看来结论是一致的就是number在构造scorer时,实际上是构造一个巨大的bitset并在上面生成一个迭代器。而keyword 在做链表时有跳表查询显然找docid要快好多。
根源就在于Lucene 6.0 对于存储number类型是用k-d tree 造成的。

看来携程的高手看Lucene还是看的很深,需要努力学习!

最后贴出作者给出的结论,如果一定要用number建索引而又不用range操作的,赶紧升级到5.4 吧:

小结:
在ES5.x里,一定要注意数值类型是否需要做范围查询,看似数值,但其实都是做Term或者Terms匹配的,应该定义为keyword字段。
如果RangeQuery的结果集很大,并且还需要和其他更加selective的查询条件做AND的,应该升级到ES5.4+,该版本在底层引入的indexOrDocValuesQuery,可以极大提升该场景下RangeQuery的查询速度。


最近因为一个索引的数据量日益暴增,达到8,9亿,因此相应的慢查询也开始显现,针对其中几种查询更是慢的离谱。
查看了一下这个慢查询的搜索条件其实也很简单,而且,简单到离谱,其中有这么两个条件查询,这两个字段的mapping都是short 类型,在没有这两个条件的查询,延迟是可以接受的,仍然能保持200ms级以内,而 is_deleted 则去到数百毫秒,is_warmup则是暴增到1~3s 不等。


这就有点奇怪了,首先docs 数一样,这两个typemapping 都是short,而且值都是只有0,1,2这样数种枚举值,那为什么这种条件的查询这么慢呢?而且还不一样?
当时自己这个问题看了大半天是无果, 做了一些假设性的猜测也就作罢, 今天好基友“聂风”同学找上门,说这几天也遇到同类的问题,也是百思不得其解,突然吊起胃口,又回过头来思考这个疑难杂症,想一看究竟。

网上有好一些有共同病患的,可惜还没有官方回答,先mark下来说不定我写完我的猜测官方就给解答了 -_-

Elastic对类似枚举数据的搜索性能优化

Why my search slow?


线索一:这两个term其实和Query的order无关

我们首先肯定会觉得这两个term的查询肯定是结果集太大了,所以并没有基于其他更小范围的filter之后做,而是并行来做,并且因为结果集很大,所以捞的时间非常大;是的,我首先就是这个想法,后来当我打开了profile:true看到了结果,表示我的猜测错了!
下面是我打开了profile 的分析结果:



从上面的分析得出,其实耗时并不是因为它并行走了索引去捞了超大量的数据导致,也不是消耗在打分上,更不是消耗在什么合并运算上,偏偏是消耗在一个build_scorer 的过程里。

这里的build_scorer 究竟是什么咚咚呢?
忘了说,我的这个慢查询都是塞在 filter里的,不是不打分的么,没看到Result的score都是0? 那和score 有半毛钱关系?
那就先挖出这个scorer的解释先:

https://www.elastic.co/guide/en/elasticsearch/reference/master/_profiling_queries.html

build_scorer

This parameter shows how long it takes to build a Scorer for the query. A Scorer is the mechanism that iterates over matching documents generates a score per-document (e.g. how well does "foo" match the document?). Note, this records the time required to generate the Scorer object, not actually score the documents. Some queries have faster or slower initialization of the Scorer, depending on optimizations, complexity, etc. This may also showing timing associated with caching, if enabled and/or applicable for the query

留意一下高亮那两句,就是说耗时是耗在了构建 一个叫Scorer Object 的东西上,并且这个东西的耗时程度取决于优化的程度(这里我理解就是建完index之后有没有做一些index的优化,比如force merge, blablabla 。。。)
好了,这个Scorer Object 一看就是Lucene的东西,所以现在暂时先不深入,再挖一下其他有用的东西。


线索二: number 字段的term操作其实都是转换成range查询

不知道上面的截图大家留意到一个细节没有,[0-0] [1-1] ,对,其实这是一个range查询



这个线索只是让我觉得有点点意外,其实本身并无太大的价值,不过也是一个思路吧,就是以后什么样的值采用number来保存。换句话说,就是当对number的操作很慢的时候,首先得想如何提高range的性能。
关于这个课题我最后再讲。


线索三:没有线索了,老老实实看代码吧!

首先看看这个时间是怎么来的:


ProfileWeight.java

从上面的说明结合得出,时间的跨度就是来生成这个scorer上,Weight 类型是一个基类,有多个实现,由于我还未曾系统的去读完整个Lucene的源码,所以这里我也只能猜了,大家都知道,Lucene 把所有的查询会转换成一颗格式化的包含各种类型查询的树,而这个Weight树我理解就是用来做合并和打分用的。
那由于这个是一个term查询,那我们就去看看TermWeightscorer方法


从代码上看,也就是说,当做某个field的检索的时候,其实Lucene会初始化一个这个field 的termEnum 这种东西,并且会调用postings 获取PostingsEnum
那这个PostingsEnum 又是什么鬼,我大概了解就是一种结果docs 的迭代器的抽象,已被到时在Lucene的结果树里可以快速做合并,运算之用

那么再对比一下,PostingsEnum的 普通term的倒排和number的倒排的实现类应该是完全不同的逻辑实现的




好,那我猜测构造慢因该就是做这个postings 慢了,因为它有各自的实现,如果涉及到docs 的指针的迭代,那么大家都知道,Lucene的倒排指向的docs 映射是用BitSet去实现的,那么到现在为止就可以猜测,要初始化一个类似LongBitSet这种东西,跟我这个number的本身的稀疏程度是否有关?


我的一种猜测

至此,我的一个猜测就是如果number类型的条件结果集很大的时候,要构造一个某种BitSet的scorer 其实是挺费劲的(像官方文档说说,还和其他很多因素相关)但是如何解释同样是number,为什么 is_warmup 要比is_deleted还要再慢几倍呢?我猜测是不同docs的这个值的稀疏程度有关,当然要回答这个还要继续深入Lucene的代码,我这里就不再继续drill down了

结论

好了,其实要解决这个问题很简单,如果是枚举类型的数字的话,mapping 用keyword 就好了,其实回顾官方文档的时候,官方文档也清楚列明了的,如果你是不做range操作的可枚举出的数字的话,比如map 的keyset,最好还是用keyword。


后话

恰巧看到一篇blog有提到官方其实一直在努力加快 number的range搜索,具体大家可以读读, 貌似这个优化增加到 ES5.4 以后的版本了

https://www.elastic.co/blog/better-query-planning-for-range-queries-in-elasticsearch

接下来看有机会debug一下Lucene的部分代码,希望大家提出自己的看法,各抒己见,就拍哪里错了误导了大家;好了,洗洗睡了!

参考文献:
https://www.compose.com/articles/how-scoring-works-in-elasticsearch/
https://qbox.io/blog/optimizing-search-results-in-elasticsearch-with-scoring-and-boosting
https://www.elastic.co/guide/cn/elasticsearch/guide/current/scoring-theory.html
https://toutiao.io/posts/4mwfeo/preview
http://www.scienjus.com/elasticsearch-function-score-query/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容