天天看点

Elasticsearch集群启动流程选举主节点选举集群元信息allocation过程数据恢复

文章目录

  • 选举主节点
  • 选举集群元信息
  • allocation过程
    • 选主分片
    • 选副分片
  • 数据恢复
    • 主分片recovery
    • 副分片recovery

集群启动过程要经历选举主节点、主分片、数据恢复等重要阶段。

Elasticsearch集群启动流程选举主节点选举集群元信息allocation过程数据恢复

选举主节点

集群启动的第一件事是选择主节点,选主之后的流程由主节点触发。ES的选主算法是基于Bully算法的改进,主要思路是对节点ID排序,取ID值最大的节点作为Master,每个节点都运行这个流程。在实现上被分解为两步:先确定唯一的、大家公认的主节点,再想办法把最新的机器元数据复制到选举出的主节点上。基于节点ID排序的简单选举算法有三个附加约定条件:

  1. 参选人数需要过半,达到quorum(多数)。
  2. 得票数需过半。
  3. 当探测到节点离开事件时,必须判断当前节点数是否过半。如果达不到quorum,则放弃Master身份,重新加入集群。如果不这么做,则可能产生双主,俗称脑裂。

集群并不知道自己共有多少个节点,quorum值从配置中读取,我们需要设置配置项:

discovery.zen.minimum_master_nodes = master节点数/2 + 1

选举集群元信息

被选出的Master和集群元信息的新旧程度没有关系。因此Master的第一个任务是选举元信息,让各节点把各自存储的元信息发过来,根据版本号确定最新的元信息,然后把这个信息广播下去,这样集群的所有节点都有了最新的元信息。为了集群一致性,参与选举的元信息数量需要过半,Master发布集群状态成功的规则也是等待发布成功的节点数过半。在选举过程中,不接受新节点的加入请求。

集群元信息不包含哪个shard存于哪个节点这种信息。这种信息以节点磁盘存储的为准,需要上报。为什么呢?因为读写流程是不经过Master的,Master不知道各shard副本直接的数据差异。HDFS也有类似的机制,block信息依赖于DataNode的上报。

allocation过程

集群元信息选举完毕后,Master发布首次集群状态,然后开始选举shard级元信息。选举shard级元信息,构建内容路由表,是在allocation模块完成的。在初始阶段,所有的shard都处于UNASSIGNED(未分配)状态。ES中通过分配过程决定哪个分片位于哪个节点,重构内容路由表。此时,首先要做的是分配主分片。

allocation过程中允许新启动的节点加入集群。

选主分片

所有的分片分配工作都是Master来做的,它向集群的所有节点询问:大家把某分片的元信息发给我。然后,Master等待所有的请求返回,然后根据某种策略选一个分片作为主分片(这种询问量=

shard数 * 节点数

,因此我们最好控制shard的总规模别太大)。

现在有了分片的多份信息,在ES 5.x以下的版本,通过对比shard级元信息的版本号来决定主分片,但这可能会将数据不是最新的分片选为主分片。因此ES 5.x开始实施一种新的策略:给每个shard都设置一个UUID,并在集群级的元信息中记录哪个shard是最新的(因为ES是先写主分片,再由主分片节点转发请求去写副分片,所以主分片所在节点的数据肯定是最新的,如果它转发失败了,则要求Master删除那个节点),然后通过集群级元信息中记录的“最新主分片的列表”来确定主分片:节点汇报信息中存在该分片,并且这个列表中也存在。

如果集群设置了:“cluster.routing.allocation.enable”: “none”。禁止分配分片,集群仍会强制分配主分片。因此,在设置了上述选项的情况下,集群重启后的状态为Yellow,而非Red。

选副分片

主分片选举完成后,从上一个过程汇总的shard信息中选择一个副本作为副分片。如果汇总信息中不存在,则分配一个全新副本的操作依赖于延迟配置项:

index.unassigned.node_left.delayed_timeout

(通常以天为单位)。

数据恢复

分片分配成功后进入recovery流程。主分片的recovery不会等待其副分片分配成功才开始recovery。它们是独立的流程,只是副分片的recovery需要主分片恢复完毕才开始。

为什么需要recovery?对于主分片来说,可能有一些数据没来得及刷盘;对于副分片来说,一是没刷盘,二是主分片写完了,副分片还没来得及写,主副分片数据不一致。

主分片recovery

由于每次写操作都会记录事务日志(translog),因此将最后一次提交(Lucene的一次提交就是一次fsync刷盘的过程)之后的translog进行重放,建立Lucene索引,如此完成主分片的recovery。

副分片recovery

副分片需要恢复成与主分片一致,同时,恢复期间允许新的索引操作。在6.0版本中,恢复分成两阶段执行。

  • phase1:在主分片所在节点,获取translog保留锁,从获取保留锁开始,会保留translog不受其刷盘清空的影响。然后调用Lucene接口把shard做快照,这是已经刷磁盘中的分片数据。把这些shard数据复制到副本节点。在phase1完毕前,会向副分片节点发送告知对方启动engine,在phase2开始之前,副分片就可以正常处理写请求了。
  • phase2:对translog做快照,这个快照里包含从phase1开始,到执行translog快照期间的新增索引。将这些translog发送到副分片所在节点进行重放。

第一阶段尤其漫长,因为它需要从主分片拉取全量的数据。在ES 6.x中,对第一阶段再次优化:标记每个操作。在正常的写操作中,每次写入成功的操作都分配一个序号,通过对比序号就可以计算出差异范围,在实现方式上,添加了global checkpoint和local checkpoint,主分片负责维护global checkpoint,代表所有分片都已写入这个序号的位置,local checkpoint代表当前分片已写入成功的最新位置,恢复时通过对比两个序列号,计算出缺失的数据范围,然后通过translog重放这部分数据,同时translog会为此保留更长的时间。因此,有两个机会可以跳过副分片恢复的phase1:基于SequenceNumber,从主分片节点的translog恢复数据;主副两分片有相同的syncid且doc数相同,可以跳过phase1。

由于需要支持恢复期间的新增写操作(让ES的可用性更强),这两个阶段中需要重点关注以下几个问题。

  • 分片数据完整性:如何做到副分片不丢数据?第二阶段的translog快照包含第一阶段所有的新增操作。那么第一阶段执行期间如果发生“Lucene commit”(将文件系统写缓冲中的数据刷盘,并清空translog),清除translog怎么办?从6.0版本开始,引入TranslogDeletionPolicy的概念,它将translog做一个快照来保持translog不被清理。
  • 数据一致性:在副分片节点,重放translog时,phase1和phase2之间的写操作与phase2重放操作之间的时序错误和冲突,通过写流程中进行异常处理,对比版本号来过滤掉过期操作。这样,时序上存在错误的操作被忽略,对于特定的doc,只有最新一次操作生效,保证了主副分片一致。

继续阅读