疲劳是最舒适的枕头,努力工作吧。

Scroll查询实现的机制是什么

Elasticsearch | 作者 Charele | 发布于2023年08月01日 | 阅读数:1272

百度了一下,有人说是快照(就是缓存),有人说是游标。
 
好像不会是缓存吧,如果有一亿条数据,你size=10每次取10条,
它不可能先把这一亿条数据存下来吧???
 
但好像又缓存了,我做了实验,比如我有1,2,3,4,5
size=1,我取到2时候把4删除了(还特地flush了一下)
 
scroll查询里,后来还是能看到这条数据的。这如何解释啊?
 
相关问题:
https://elasticsearch.cn/question/11105 
 
图片与主题无关
111.PNG
已邀请:

pzw9696

赞同来自: Charele emmning

就是快照,这个快照是Shard级别的一个SearchContext,缓存在内存,里面记录了scroll上一次查询在这个shard维度的docid和打分(lucene维度),用于下一次查询的时候直接跳过不必要遍历的文档或者段,至于你说删除了,为啥还能查到,那是因为SearchContext同时把用于lucene查询的那个段对象(IndexReader)也缓存了。

Charele - Cisco4321

赞同来自:

昨天看了大半夜的代码,有了一点认识。
说下我的看法,有错误的地方,如果有希望有人修正。
 
我是用 a/_search?scroll=1h&size=1来测试的
(没用到slice功能,不知道在slice情况下是否有变化)
 
1 有人说scroll_id会变,所以每次要用上次得到的最新得到的scroll_id
我没看到变的情况啊,啥情况时会变呢?
 
2 scroll查询只是提供了一个方便的分页查询的手段,
它本身并没有任何对查询优化(或者提升性能)的机制。
它仅仅缓存的只是上一次查询时最后的那个文档号,这个号只是用来判断的。
对性能没有任何帮助。
 
由于查询语句没有任何变化(有一个在特定sort条件下查询变化的除外)
 所以每次传给收集器的文档号都是一样的,只是拿缓存的那个文档号来判断。
比如你有一万个文档(号是1-10000),查询时size=100,用matchAll
 
第一次查时,它是查一编,得到号是1-10000传给收集器,收集器在看100时,就结束了
(这1-10000不一定要先保存起来,可能是迭代器之类的,这不重要)
 
第二次查时,它还是得查一编,得到号还是1-10000传给收集器。收集器要在看完1-100后,才开始真正有行动,
,,,
最后一次时,它还是得查一编,得到号还是1-10000传给收集器。收集器要看完1-9900后,才会行动。
 
所以我感觉这东西,如果数据量很大,理论上,越翻到后面,速度会越慢。
 
我想这也是个的答案,因为scroll查询并没有任何性能上的考量
https://elasticsearch.cn/question/11105

Charele - Cisco4321

赞同来自:

查询了一下论坛中和scroll相关的问题,有这么一个结论,
使我很不理解。
111.PNG

 
1 他的观点是”Query轻量级,Fetch是重量级的“,
可能有时fetch时要合并排序结果比较重,
但不是所有查询都需要这个操作。
而Query部分,复杂查询,比如各种bool,模糊查询组合啥的啥的,才是耗时的主要部分
 
我的观点是:相比来说,query部分才是大头。
 
2 他说scroll第一次查的时候,“doc_id 列表会保存在上下文列表里”,
等于把所要的所有文档号缓存了,后面直接取就行了。
存在哪?我没找到
我看到的只是“前一次查询时最后那一个”的文档号 
我看的是8.2.3,是新老版本实现的差异吗
 
 

charlesfang

赞同来自:

size=1,我取到2时候把4删除了(还特地flush了一下)
----
flush是落盘,应该执行一下refresh才能保证可读,不确定删除4是不是因为没有refresh,所以还是可以查到

Charele - Cisco4321

赞同来自:

针对你所说的两点,我说明一下
 
1 “最后一次时,它还是得查一编,得到号还是1-10000传给收集器。收集器要看完1-9900后,才会行动”
 
我没有说收集器要去处理1-9900这些文档(就是你说的那个"进queue"),
它只是非得要看一下才pass。
 
假如你有1亿条数据,最后一次查询时(size=100),
前面的9999万9900个文档号,它都得看一下,都得空转一下。
所以我觉得理论上,越翻后会越慢。
 
 注:排序情况不在这讨论
 
2 不是refersh的原因,
首先,缺省1秒一次的,肯定是refersh过的了。
为了明确结果,我连forceMerge都用上了。
 
原因在于楼上所说,在第一次scroll查询时,它把dr缓存了。
后继关联查询全是用的这个老的dr(即使你refresh换成了新的dr,但我这里不变)
111.PNG

Charele - Cisco4321

赞同来自:

截图来自PagingTopScoreDocCollector这个类
111.PNG

 
蓝色处,所有的文档号都会进来
 
(至于这里是不是每次查询都会进所有文档号???在这里打印一下doc,一测便知)
  红的地方,不合格的文档会返回。不理它们。
 
我的说“空转”就是这个,现在已经形成
, 你所说的pq还在下面的,还没碰到呢,怎么拦???
只在合格的文档号(本次查询应该处理的那一块文档号)才会在下面进pq后续,,,
  
我所说的,慢,理论上的。
比如,1到1万的空转,可能实际执行中你感觉不出来。
1到1亿呢?实际是没有人会翻完1亿条数据
 
 另外,refresh操作,本质上就是换一个dr.
(实际上是换两个:-)
 执行NRT生成一个新的,把ReferenceManager里面的dr对像换成这个新的,
后面执行的查询,就会用这个新的dr
 

Charele - Cisco4321

赞同来自:

对,这里是跳出了,是你说的“是不是翻页到这里的时候越到后面拦得就越多”,
但这里要花时间去判断一下后才能拦啊
 
因为越到后面,要判断的无效文档号就越多,
,你去判断100个可能不花时间,但判断1个亿呢?
 这就是我的观点“翻后面的要比翻前面的慢”要表达的意思。
 
(也许我的这个想法是错的, 不确定)
 
 另外,查询部分越复杂,scroll查询是越慢。
但这跟翻前面和翻后面,是无关的。
因为不管是翻面前还是翻后面,先要执行的这块是一样的。
 
  查询部分复杂还是简单,跟我要表达的没有关系。

Charele - Cisco4321

赞同来自:

啊???我并没有说不要这个“after逻辑”啊。
 
设想这么一个场景,1万条数据,你作一个scroll查询,size=10,
我现在就想查一下所有数据,就用最简单的matchAll,可以吗?
 
(如果你说,不行!scroll查询就非得用复杂查询,排序打分啥的,那我没话可说) 
 
比如你是一个医生,有一万个病人要看病(他们都是编好号的,1,2,3,,,,10000)。你每天只看10个。
 
第一天,这一万个病人会依次进门,你就看,1号,2号,,,10号,
好,看完了,这这一天的工作就完成了
你记录了第一天看的最后一个病号10
 
第二天,这一万个病人还是排着队要进门
(每天都一样,每天要进门的病人并没有减少)
 
 你看到1号病人,你就拿病号跟昨天记录的病号10比较一下,知道这个病人看过了,让他出去。
2,3,,10,都是如此
  一直到11号进来了,你知道这个病人没看过,所以就给他看病
(干你真正的医生工作,就等于ES中收集器真正要做的事,进pq啥的那些后续,,,)
 
好了,等看完20号,你今天的任务完成了。
最后,你记录了最后看的一个病号20
 
注意:今天,那1号-10号病人你并没有看病,让他们出去了,
就像你说的“把他们拦住了”,
 但是接待这10个病人不要花你的时间啊?
 
你要判断一下他们是不是看过了,然后花你的时间说“亲,请出去”。这是要花时间的。 
,,,,,
 
 最后一天,所有病人还是都会排着队要进门。
这1号-9990号病人,你都得接待一下,然后跟他们说“请出去”,
然后再最后一批真正要看的病人,9991号-10000号病人。
 
你会发现,越往后,你要说的“请出去”的次数会越来越多。
第一天,你不用说。你花的时间就是看10个病人的时间,
第二天你要说10次,,,时间就是 看10个病人 + 说10次“请出去”
 最后一天,时间就是 看10个病人 + 说9990次“请出去”
 
 
你看看我说的这医生的事,和刚我说的scroll查询的场景,
是不是一样的?
如果有不一样,请指出来。。。。

要回复问题请先登录注册