即使是不成熟的尝试,也胜于胎死腹中的策略。

如何深入理解ES的分片

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

这是一个沉重的话题,这涉及到ES的方方面面,
因为ES本身就是围绕分片来操作的。
 
我想找一个切入点来谈这个问题(只是一个点,也许能延伸到面吧)
如果有人看出哪个地方有错误,可以指出来。
  
我用_flush操作作为切入点,flush可能每个人都操作过,

此方法来自
org.elasticsearch.index.engine.InternalEngine.commitIndexWriter(IndexWriter, Translog)

222.png

 
这是每次_flush时做的事之一
这里定义了一个map, 然后put后相应的key和value
然后写进硬盘文件。
(问题是,写到哪里?如果你学过Lucene,就明白写到哪了)
 
问题1 这8个东西分别是哪来的
问题2 这8个东西的会在什么时候取出来用,就是说作用是什么????
 
虽然说,这些,只是理解ES(分片)的一个小点,
但不深刻理解这些,你绝对不能说自己理解ES!
 
  比如LOCAL_CHECKPOINT_KEY,
看名字好像是本地检查点,
这是本地的。那另外那个全局检查点又存在哪里呢?
已邀请:

Charele - Cisco4321

赞同来自:

1
我下面会分别介绍每个参数,包括它的来源,如何变化的,以及作用。
 
ES_VERSION这个,没什么重要作用,就不说了。
 
不可能说得面面俱到,因为涉及的代码太多了。只是重要的点提一下,
要完全搞明白,还得自己看代码去理解。
 
111.png

首先上一个图,说明一下,
每个分片都会产生这么两个组件(其实很多,现在只关注这两个)
那个“专注于,,,”,只是粗略的说明一下,并不准确完整 
 
 另外说明一下,InternalEngine这个组件,不一定是这个,
比如当你的索引是CCR里面的follower索引时,是FollowingEngine,因为follower索引有一些特殊之处,当然,这是题外话。

不过仍是InternalEngine的子类。
 

Charele - Cisco4321

赞同来自:

2
在说LOCAL_CHECKPOINT_KEY之前,先来说一下日志恢复。
 
大家在看网上相关文章的时候,都会看到,副分片在启动时会进行日志恢复,
 (副分片恢复在代码里是属于"PEER恢复“,
另外,发生balance时,主分片的恢复也是属于PEER类型,也需要从远程节点拉取日志,这里不谈这种情况)
 
其实主分片在启动时,也会进行日志恢复。
不妨称: A类恢复, B类恢复(这只是方便述说)
 比如:你写了3条数据,还没来得及flush,ES挂了。
重启ES后,就需要从日志中读这3条数据的日志来恢复数据
(缺省QUEST下,“同步全局检查点”这个操作就保证了日志肯定会存盘了)
这就是A类的恢复。
111.png

 
 
副分片在启动时,会同时“要求”经历上面A类,B类两种恢复
设想一下场景:
1> 写入3条数据,还没有flush
2> 这时副分片节点挂了
3> 然后主分片又写入4条数据
4> 然后启动了副分片节点
 
副分片在启动时,首先会进行A类恢复那3条数据,这时取的日志是本地的,
(主分片,和副分片执行A类恢复时,执行的不是同一个方法)
 然后会从主分片拉取日志,恢复那4条数据,
这就是B类恢复,也是一般意义上人们常说的副分片的日志恢复,即"phase2"里那个里做的事
 
副分片上的A类恢复,是发生在副分片开始“正式恢复”操作之前,这一般会被人们所忽视。

Charele - Cisco4321

赞同来自:

3
上面说了些关于LOCAL_CHECKPOINT_KEY一些作用,下面来谈,这个8变量之一,是如何产生和演变的
111.png

 
这个LocalCheckpointTracker就是上面我说的组件图里的,
(大园,小园,表示包括在里面)
构造时传入了两个参数maxSeqNo, localCheckpoint,
(maxSeqNo也是“8变量”之一)
 
如果是旧分片,这两个值就是从磁盘中读入的(由上次flush时写入)
如果是新分片,这两个值是-1
 
令人困惑的时,下面有两个checkpoint变量,
都是指的本地检查点,
一个是“处理过的”,一个是“持久化过的”。
 
简单解释下:
SeqNo就是每来一个操作,给你分配一个序号。
processedCheckpoint,如果设为123,说明123(和以前)号的操作都处理过了
persistedCheckpoint,如果设为456,说明456(和以前)号的操作都持久化了

Charele - Cisco4321

赞同来自:

4 另外提一下,
上面那个绿色的地方,
nextSeqNo.set(maxSeqNo + 1),如果是新分片,这是-1 + 1 = 0;
111.png

 
如果你在新分片上执行一个插入,这也是这儿你看到0的原因。
 
你也许会问,那另外两个(红色的)为什么不这么搞一下呢?新分片,这两个值难道从-1开始吗?
不是的,为什么不用,后面会谈到

Charele - Cisco4321

赞同来自:

5 下面继续
先补一下相关知识
111.png

这个类,说明了某个操作(插入,删除啥的)的来源。
一目了然。
 唯一需要注意的是:PEER_RECOVERY用在副分片恢复中,
即上面的B类恢复,它是用来恢复日志的,
但它不认为是“从日志来的”!
 
从日志来的只有最后两种,
其中,LOCAL_TRANSLOG_RECOVERY就是上面说的A类恢复
 

Charele - Cisco4321

赞同来自:

6 接上楼
图中代码来自InternalEngine.index(Index)方法
这是在插入一个文档之后做的事(文档已经插入了)
 
111.png
222.png

 下面为了说清楚一点,分两种情况:
A 写主分片
1> 因为这不是从日志来的(原因见上面的Origin类),所有在绿处执行写日志的行为
2> 执行蓝处的动作,推进“已处理过的本地点”(就是把processedCheckpoint变量设为这个值)
3> 因为上面写了日志,所以紫处为假,
所以不执行黄处的 推进”已持久化的本地点“
 
B 本地日志恢复(见上面的A类恢复)
1> 因为是从日志来的,因为日志里已经有了此操作,所以无需写日志
2> 执行蓝处的动作,这个同上面一样
3> 因为上面没写日志的行为,所以紫处为true,执行黄处的
推进“已持化的本地点”,因为操作本来就是从日志里取出来的,肯定是已经持久化了的,所以可以放心地执行这个
 
这里两个本地点,指的就是LocalCheckpointTracker类里面那两个变量。
 
  有个要特别说明的是:为什么在情况A下,不推进“已持化的本地点”呢???
因为这里只是写了日志(日志仅仅在内存中),并不能保证这个本地点的日志已经持久化了。
这个动作要等到后面持久化日志时才能确定。
 
 
 

Charele - Cisco4321

赞同来自:

7
所以这两个本地点,是“没有自己的值的”,
只是在“推进”XXX点时,用SeqNo传进去,看下,如果比自己大,就更新成这个SeqNo。
 
SeqNo在每个操作时都会递增的
你不断执行插入操作(一个分片时),会发现结果那个"_seq_no"会递增+1
(多个分片时,不一定,因为下个插入的文档,也许就是另外一个分片上的操作了)
111.png

 
 

Charele - Cisco4321

赞同来自:

8 接6楼
 6楼说道,当执行一个普通的文档插入操作时,是不推进“已持久化本地点的”,就是有新值也不更新。
那它是啥时更新的呢。
先看它,它的预处理:
6楼绿处的加日志的方法里,进去后你会发现,
111.png

它会存到一个叫nonFsyncedSequenceNumbers的集合里面。
 
这个集合啥时处理呢?
在“同步全局检查点”的时候。那是啥时候呢。
你肯定知道这个参数: "index.translog.durability": "async"
它缺省是"quest",就是每个插入操作后都会执行,而"async"下,是缺省5秒执一次的。
(同步全局点,很多情况都会执行它,但这里只谈最常见的发生情况)
 
 

Charele - Cisco4321

赞同来自:

9
下面这个图就是说:缺省quest情况下,会执行"同步全局点“
但这个方法,在"async"的时候,是无效的。这个可以自己琢磨。
111.png

 
  
在"async"的时候,ES会启一个后台线程,每5秒,去执行一下"同步全局点“
222.png

这里的getTranslogSyncInterval(),就是那个5秒时长。
 
下面说下“同步全局点”这个操作,它做了4件事:
 
1> 把日志存盘。(注意:它并不roll日志文件,这是flush做的事)
2> 写translog.ckp,全局检查点文件
(translog.ckp文件里面包括全局检查点,全局检查点,它内存中是存在ReplicationTracker这个组件里的,看上面的组件图。
怎么计算得来的,有点复杂,后面会讲)
3> 就是上面说的,推进“持久化的本地点”
4> 保存上面的Checkpoint
 
111.png

这个flushedSequenceNumbers,等于是8楼的那个集合。
因为上面日志已经存盘了,现在可以放心的说:“这些点的日志已经持久化了” 
 
 
 
 
 

Charele - Cisco4321

赞同来自:

10 
上面说了那“8变量”中的两个,下面说说另一个重要的东西:全局检查点(不是那8个之一)
有两个全局点,一个是硬盘中的(就是已存过盘的),一个是内存中的(即当前的,下一次会存盘的)
这两个有时会相互比较,例如,见下:
因为如果100都存过盘了,99就没必要再存盘了
333.png

 
 
1> 硬盘中的,是存在translog.ckp中的,translog.ckp大小是固定的,
写translog.ckp就是写一个Checkpoint类,读硬盘中的全局点时,并不是每次都去读translog.ckp,没必要。
它会把上次存盘的Checkpoint类作为一个变量,读这个变量里的数据就可以了。

  
 

要回复问题请先登录注册