橡皮、老虎皮、狮子皮哪一个最不好?

如何深入理解ES的merge策略

Elasticsearch | 作者 Charele | 发布于2022年05月25日 | 阅读数:1871

发这个贴子,是因为网上搜不到内容,我想可以写出来让别人了解一下。
 
网上能搜的都是讲为什么要merge,merge的好处等等,还有一些千篇一律的参数设置,
还有就是重复Lucene的merge策略那些东西。

关于Lucene(也包括merge),可以看看这位大神的文章
https://www.amazingkoala.com.c ... .html
 
 我要说的是关于ES自己的merge策略:
222.png

ES把Lucene的策略进行了自己的层层包装,一共有5层,我会解释每一层的作用。
 如果有错误,请指出。
已邀请:

Charele - Cisco4321

赞同来自:

1
先说第一个红的,它是最简单直观的
555.png

它包含了两个策略,常规的和force的(下称p1, p2),特别的,在构造里,给p2设了一个无穷大参数。
这个设数是设置“最大段大小的”,就是说如果一个段的大小超过了这个值(缺省5g) ,就不应该被merge了。
(注意:p1是被传去父类里去的,就是说如果没显式指明的,动作都是由p1来完成的)
 
  红色地方很清晰了,把找“force段"的功能委派给了p2。它的意思是说,当你执行"forceMerge"的时候,没有这个5g限制。
 
为什么这么做,源码上面有git地址,感兴趣的同学可以看看(反正我从来不上git) 
另外:不是说你在ES里执行"_forcemerge"操作的时候,就一定是“forceMerge”动作,由你的参数来决定,这个可以自己看代码。

Charele - Cisco4321

赞同来自:

2
那当我在ES参数里设置“最大段大小”的时候,会怎么样呢?
它只对常规的起作,不管force的那个。
777.png

 
第一个说完了,此策略本身简单,
不过里面的TieredMergePolicy复杂的,想了解,可以看楼上的链接。

Charele - Cisco4321

赞同来自:

3
现在说第二个策略
111.png

先说下面蓝色的,这是一个查询,
(先多方比较,找到一个最小值),"_seq_no"大于这个值的文档都命中。
这个贴子的6楼也说了这个:
https://elasticsearch.cn/question/12634
 
然后红色的,是"_recovery_source"这个字符串,这个串是做什么用的呢。
我们知道"_source"(在查询结果会看到它),它缺省是开启的。它是可以关闭的。那如果关闭了,日志恢复啥的,都要依靠它,不是不能进行了么?
如果关闭了,ES会把相同的内容,存在一个叫"_recovery_source"的字段里,用来进行恢复。

222.png

红色是通常的情况,存"_source"的时候,
绿色的是关闭的情况,存"_recovery_source"
(或者有两个都存,或两个都不存的情况,不深究了呵呵) 
另外,你会看蓝色里面是数字1,为什么是1呢?其实这是一个标记,有就行。就跟那个softDelete标记一样。
 
注:这个"_recovery_source",只是用来做恢复的,没有其他用途。你不会在search结果中看到它,也不会长久保存。如果ES认为恢复用不到这个文档的"_recovery_source"字段了,就会删除它。
 
这个策略就是用来做这个的

Charele - Cisco4321

赞同来自:

4
继续楼上
首先说下,策略2,3,4都是Lucene OneMergeWrappingMergePolicy的子类,
这个Lucene类的大概意思就是,找出哪些保留,哪些丢弃。
 
如果你不了解OneMergeWrappingMergePolicy的执行逻辑,也不要紧。
关注于ES策略本身的判断就可以了。
111.png

紫色的就是查出关于楼上说的"_recovery_source"的docValues值列表,
绿色表示,这个段里面没有任何文档有"_recovery_source"这个字段,
我无需要处理这个段,直接传给下一个策略。
(另外:只关心有没有,并不在乎它的docValues值是多少,如楼上所讲的那样)
 
黄色部分,是Lucene里的语法,就是根据一个查询建一个打分器,
查询,就是3楼那个蓝色的查询:
下面分别说:
1> scorer != null
说明段中有符合查询的文档,
然后建了一个BitSet,(BitSet:里面为1就是符合查询,为0就是不符合)
蓝色处说明,这个段里的文档都符合那个查询条件,都要保留。
不用再过滤了。我不管了,直接传给下一个策略吧。
 
橙1处说明,有些符合,有些不符合,不符合的那些要过滤掉。
 
2> scorer 为null
橙2处说明,这个段里面,
有"_recovery_source"字段的文档,但文档都不符合那个查询,
这种情况也需要处理,同橙1一样。
只不过传入的BitSet对像参数是null,不关心哪些要过滤,因为已知所有文档都不符合了。
 
下面来看看后续,就是SourcePruningFilterCodecReader

Charele - Cisco4321

赞同来自:

5
简单的说,就是跟据那个BitSet来过滤一下,
 该留的留,该走的走。截图大概示例一下:
111.png

绿色说明(楼上橙2处),文档都不符合,给一个空的迭代器,则所有文档的docValues值不要保留了,因为没用了。

这个SourcePruningFilterCodecReader类有两个方法,上面说的是是处理docValues的情况,
下面还有一个是处理"store"值的逻辑,感兴趣的人可以自己看下。太简单不说了
333.png

Charele - Cisco4321

赞同来自:

6
上面的说完了,下面来到了第3个策略:org.apache.lucene.index.SoftDeletesRetentionMergePolicy
这个类,不是ES的,是Lucene的。
 
这个策略,就是ES或者说Lucene里的软删除那些东西,看看网上的相关文章就能明白在干什么。
软删除的作用不用说了。
 
大概思想就是:把那些本应该删除的(就是加上softDelete标记的),但符合查询条件的文档的保留下来。

Charele - Cisco4321

赞同来自:

7
下面讲第4个策略
111.png

跟第2个策略,比起来,这个要简单一点。
难点是理解为什么要执行这个,它在里面起啥作用

Charele - Cisco4321

赞同来自:

8
222.png

首先看看它的说明,用来保证update的性能。为什么它有这个功能呢。
对这个问题的认识,我没有完全搞清楚。
 PrunePostingsMergePolicy,从名字来看是PrunePostings,
没错,它就是这个意思,它是把文档的posting裁剪掉。
 
posting,就不解释是啥了,一句两句也说不清。
 
就是doc,pos,pay文件里那些东西。
比如doc里存放了term(或者说分词过后的词)的文档号和词频等等

Charele - Cisco4321

赞同来自:

9

7楼上面标明了,要裁剪就是"_id",就是我们最熟悉的文档号这个字段,看下它的定义
111.png

它是不分词的,
绿色里的DOC就表示它是可被索引的,也就是说它会产生posting。
 
这个策略的意思,就是说把已删除文档里的"_id"的posting信息删除掉。
下面看具体实现:
 

Charele - Cisco4321

赞同来自:

10
444.png

红色就是这个段里有没有被删除的文档,如果没有,那就不是我们的责任了,直接扔给别人
绿色为true,是代表这个段里没有任何文档(下面有对应的处理)
 


红色表示,要是"_id"这个字段,我们才处理,否则我们不理睬
绿色表示,段里没任何文档,迭代over。
蓝色的,表示接受,哪些接受,看楼下的分析 
 

Charele - Cisco4321

赞同来自:

11
这个方法在OnlyLiveDocsPostingsEnum这个类里面
222.png

看红色的,首先说一下liveDocs的含义,它是一个BitSet
一般不删除文档的话,是不会出现它的。如果有删除(软删除,硬删除都一样)就会有它。
live是活下来的意思,
没删除的文档号会置为true,表示活下来了
被删除的文档号会置为false。
 
 while()里面的逻辑就是,如果liveDocs里面这个文档是false,它会一直loop,
直到找到liveDocs里是true,然后把文档号提供出去,
然后这个(docId代表的)文档的("_id"这个字段的)posting信息会用于merge,保留下来。
 
注意:图里的docId是int,是真正的Lucene的(段里)的文档号
而"_id",是字符串,它的值是ES层面的文档号。
(从Lucene的角度来看,"_id"和用户的字段,比如"name", "age"一样,没区别。
只是在ES这层,加了一个"_"前缀,和用户定义的字段区分开来)
444.png

这两者要区分清楚。
(这个报错是说,"_id"这个字段名,ES已经用了,你不能再用了) 
 
 换言之,如果这个文档被删了,它的docId就不会return,
也就是说,最后合并posting的时候,就不会出现这个文档号(里的"_id"字段的posting),也就是就丢弃掉了。。。

Charele - Cisco4321

赞同来自:

12

说完了它的执行逻辑,其实很简单。
注意:不是把这个文档删除!!!,而是把这个文档里的_id字段的posting信息丢掉。
被删除的文档是要保留的,保留到啥时,由ES里面软删除定义的规则来定(见顶楼)
 
至于8楼说的 “用来保证update的性能”,
如果是update操作,它肯定要找原有的文档(就是_id一样),来形成version值这些额外操作,
 
而如果_id字段的posting信息没有了,就找不到这个文档了
(因为posing信息就是我们知道的倒排索引,找文档号用的), 所以就省去了上面的那些步骤
 
这是我的理解,希望有想法的同学补充或修正一下。
 
下面说最后一个策略:
 

要回复问题请先登录注册