Elasticsearch分布式一致性原理剖析(一)-节点篇

摘要: ES目前是最流行的开源分布式搜索引擎系统,其使用Lucene做为单机存储引擎并提供强大的搜索查询能力。学习其搜索原理,则必须了解Lucene,而学习ES的架构,就必须了解其分布式如何实现,而一致性是分布式系统的核心之一。html

前言
“Elasticsearch分布式一致性原理剖析”系列将会对Elasticsearch的分布式一致性原理进行详细的剖析,介绍其实现方式、原理以及其存在的问题等(基于6.2版本)。 node

ES目前是最流行的开源分布式搜索引擎系统,其使用Lucene做为单机存储引擎并提供强大的搜索查询能力。学习其搜索原理,则必须了解Lucene,而学习ES的架构,就必须了解其分布式如何实现,而一致性是分布式系统的核心之一。
本篇将介绍ES的集群组成、节点发现与Master选举,错误检测与扩缩容相关的内容。ES在处理节点发现与Master选举等方面没有选择Zookeeper等外部组件,而是本身实现的一套,本文会介绍ES的这套机制是如何工做的,存在什么问题。本文的主要内容以下:算法

  1. ES集群构成
  2. 节点发现
  3. Master选举
  4. 错误检测
  5. 集群扩缩容
  6. 与Zookeeper、raft等实现方式的比较
  7. 小结

ES集群构成

首先,一个Elasticsearch集群(下面简称ES集群)是由许多节点(Node)构成的,Node能够有不一样的类型,经过如下配置,能够产生四种不一样类型的Node:api

conf/elasticsearch.yml:
    node.master: true/false
    node.data: true/false

四种不一样类型的Node是一个node.master和node.data的true/false的两两组合。固然还有其余类型的Node,好比IngestNode(用于数据预处理等),不在本文讨论范围内。安全

当node.master为true时,其表示这个node是一个master的候选节点,能够参与选举,在ES的文档中常被称做master-eligible node,相似于MasterCandidate。ES正常运行时只能有一个master(即leader),多于1个时会发生脑裂。服务器

当node.data为true时,这个节点做为一个数据节点,会存储分配在该node上的shard的数据并负责这些shard的写入、查询等。session

此外,任何一个集群内的node均可以执行任何请求,其会负责将请求转发给对应的node进行处理,因此当node.master和node.data都为false时,这个节点能够做为一个相似proxy的节点,接受请求并进行转发、结果聚合等。架构

上图是一个ES集群的示意图,其中Node_A是当前集群的Master,Node_B和Node_C是Master的候选节点,其中Node_A和Node_B同时也是数据节点(DataNode),此外,Node_D是一个单纯的数据节点,Node_E是一个proxy节点。并发

到这里,咱们提一个问题,供读者思考:一个ES集群应当配置多少个master-eligible node,当集群的存储或者计算资源不足,须要扩容时,新扩上去的节点应该设置为什么种类型?app

节点发现

Node启动后,首先要经过节点发现功能加入集群。ZenDiscovery是ES本身实现的一套用于节点发现和选主等功能的模块,没有依赖Zookeeper等工具,官方文档:

https://www.elastic.co/guide/...

简单来讲,节点发现依赖如下配置:

conf/elasticsearch.yml:

discovery.zen.ping.unicast.hosts: [1.1.1.1, 1.1.1.2, 1.1.1.3]

这个配置能够看做是,在本节点到每一个hosts中的节点创建一条边,当整个集群全部的node造成一个联通图时,全部节点均可以知道集群中有哪些节点,不会造成孤岛。

官方推荐这里设置为全部的master-eligible node,读者能够想一想这样有何好处:

It is recommended that the unicast hosts list be maintained as the list of master-eligible nodes in the cluster.

Master选举

clipboard.png

上图是一个ES集群的示意图,其中Node_A是当前集群的Master,Node_B和Node_C是Master的候选节点,其中Node_A和Node_B同时也是数据节点(DataNode),此外,Node_D是一个单纯的数据节点,Node_E是一个proxy节点。

到这里,咱们提一个问题,供读者思考:一个ES集群应当配置多少个master-eligible node,当集群的存储或者计算资源不足,须要扩容时,新扩上去的节点应该设置为什么种类型?

节点发现

Node启动后,首先要经过节点发现功能加入集群。ZenDiscovery是ES本身实现的一套用于节点发现和选主等功能的模块,没有依赖Zookeeper等工具,官方文档:

https://www.elastic.co/guide/...

简单来讲,节点发现依赖如下配置:

conf/elasticsearch.yml:

discovery.zen.ping.unicast.hosts: [1.1.1.1, 1.1.1.2, 1.1.1.3]

这个配置能够看做是,在本节点到每一个hosts中的节点创建一条边,当整个集群全部的node造成一个联通图时,全部节点均可以知道集群中有哪些节点,不会造成孤岛。
官方推荐这里设置为全部的master-eligible node,读者能够想一想这样有何好处:

It is recommended that the unicast hosts list be maintained as the list of master-eligible nodes in the cluster.

Master选举

上面提到,集群中可能会有多个master-eligible node,此时就要进行master选举,保证只有一个当选master。若是有多个node当选为master,则集群会出现脑裂,脑裂会破坏数据的一致性,致使集群行为不可控,产生各类非预期的影响。

为了不产生脑裂,ES采用了常见的分布式系统思路,保证选举出的master被多数派(quorum)的master-eligible node承认,以此来保证只有一个master。这个quorum经过如下配置进行配置:

conf/elasticsearch.yml:
    discovery.zen.minimum_master_nodes: 2

这个配置对于整个集群很是重要。

加粗文字1 master选举谁发起,何时发起?
master选举是由master-eligible节点发起,当一个master-eligible节点发现知足如下条件时发起选举:

该master-eligible节点的当前状态不是master。
该master-eligible节点经过ZenDiscovery模块的ping操做询问其已知的集群其余节点,没有任何节点链接到master。
包括本节点在内,当前已有超过minimum_master_nodes个节点没有链接到master。
总结一句话,即当一个节点发现包括本身在内的多数派的master-eligible节点认为集群没有master时,就能够发起master选举。

2 当须要选举master时,选举谁?
首先是选举谁的问题,以下面源码所示,选举的是排序后的第一个MasterCandidate(即master-eligible node)。

public MasterCandidate electMaster(Collection<MasterCandidate> candidates) {
    assert hasEnoughCandidates(candidates);
    List<MasterCandidate> sortedCandidates = new ArrayList<>(candidates);
    sortedCandidates.sort(MasterCandidate::compare);
    return sortedCandidates.get(0);
}

那么是按照什么排序的?

public static int compare(MasterCandidate c1, MasterCandidate c2) {

// we explicitly swap c1 and c2 here. the code expects "better" is lower in a sorted
// list, so if c2 has a higher cluster state version, it needs to come first.
int ret = Long.compare(c2.clusterStateVersion, c1.clusterStateVersion);
if (ret == 0) {
    ret = compareNodes(c1.getNode(), c2.getNode());
}
return ret;

}
如上面源码所示,先根据节点的clusterStateVersion比较,clusterStateVersion越大,优先级越高。clusterStateVersion相同时,进入compareNodes,其内部按照节点的Id比较(Id为节点第一次启动时随机生成)。

总结一下:

当clusterStateVersion越大,优先级越高。这是为了保证新Master拥有最新的clusterState(即集群的meta),避免已经commit的meta变动丢失。由于Master当选后,就会以这个版本的clusterState为基础进行更新。(一个例外是集群所有重启,全部节点都没有meta,须要先选出一个master,而后master再经过持久化的数据进行meta恢复,再进行meta同步)。
当clusterStateVersion相同时,节点的Id越小,优先级越高。即老是倾向于选择Id小的Node,这个Id是节点第一次启动时生成的一个随机字符串。之因此这么设计,应该是为了让选举结果尽量稳定,不要出现都想当master而选不出来的状况。
3 怎么算选举成功?
当一个master-eligible node(咱们假设为Node_A)发起一次选举时,它会按照上述排序策略选出一个它认为的master。

假设Node_A选Node_B当Master:
Node_A会向Node_B发送join请求,那么此时:

(1) 若是Node_B已经成为Master,Node_B就会把Node_A加入到集群中,而后发布最新的cluster_state, 最新的cluster_state就会包含Node_A的信息。至关于一次正常状况的新节点加入。对于Node_A,等新的cluster_state发布到Node_A的时候,Node_A也就完成join了。

(2) 若是Node_B在竞选Master,那么Node_B会把此次join看成一张选票。对于这种状况,Node_A会等待一段时间,看Node_B是否能成为真正的Master,直到超时或者有别的Master选成功。

(3) 若是Node_B认为本身不是Master(如今不是,未来也选不上),那么Node_B会拒绝此次join。对于这种状况,Node_A会开启下一轮选举。

假设Node_A选本身当Master:
此时NodeA会等别的node来join,即等待别的node的选票,当收集到超过半数的选票时,认为本身成为master,而后变动cluster_state中的master node为本身,并向集群发布这一消息。

有兴趣的同窗能够看看下面这段源码:

if (transportService.getLocalNode().equals(masterNode)) {
            final int requiredJoins = Math.max(0, electMaster.minimumMasterNodes() - 1); // we count as one
            logger.debug("elected as master, waiting for incoming joins ([{}] needed)", requiredJoins);
            nodeJoinController.waitToBeElectedAsMaster(requiredJoins, masterElectionWaitForJoinsTimeout,
                    new NodeJoinController.ElectionCallback() {
                        @Override
                        public void onElectedAsMaster(ClusterState state) {
                            synchronized (stateMutex) {
                                joinThreadControl.markThreadAsDone(currentThread);
                            }
                        }

                        @Override
                        public void onFailure(Throwable t) {
                            logger.trace("failed while waiting for nodes to join, rejoining", t);
                            synchronized (stateMutex) {
                                joinThreadControl.markThreadAsDoneAndStartNew(currentThread);
                            }
                        }
                    }

            );
        } else {
            // process any incoming joins (they will fail because we are not the master)
            nodeJoinController.stopElectionContext(masterNode + " elected");

            // send join request
            final boolean success = joinElectedMaster(masterNode);

            synchronized (stateMutex) {
                if (success) {
                    DiscoveryNode currentMasterNode = this.clusterState().getNodes().getMasterNode();
                    if (currentMasterNode == null) {
                        // Post 1.3.0, the master should publish a new cluster state before acking our join request. we now should have
                        // a valid master.
                        logger.debug("no master node is set, despite of join request completing. retrying pings.");
                        joinThreadControl.markThreadAsDoneAndStartNew(currentThread);
                    } else if (currentMasterNode.equals(masterNode) == false) {
                        // update cluster state
                        joinThreadControl.stopRunningThreadAndRejoin("master_switched_while_finalizing_join");
                    }

                    joinThreadControl.markThreadAsDone(currentThread);
                } else {
                    // failed to join. Try again...
                    joinThreadControl.markThreadAsDoneAndStartNew(currentThread);
                }

按照上述流程,咱们描述一个简单的场景来帮助你们理解:

假如集群中有3个master-eligible node,分别为Node_A、 Node_B、 Node_C, 选举优先级也分别为Node_A、Node_B、Node_C。三个node都认为当前没有master,因而都各自发起选举,选举结果都为Node_A(由于选举时按照优先级排序,如上文所述)。因而Node_A开始等join(选票),Node_B、Node_C都向Node_A发送join,当Node_A接收到一次join时,加上它本身的一票,就得到了两票了(超过半数),因而Node_A成为Master。此时cluster_state(集群状态)中包含两个节点,当Node_A再收到另外一个节点的join时,cluster_state包含所有三个节点。

4 选举怎么保证不脑裂?
基本原则仍是多数派的策略,若是必须获得多数派的承认才能成为Master,那么显然不可能有两个Master都获得多数派的承认。

上述流程中,master候选人须要等待多数派节点进行join后才能真正成为master,就是为了保证这个master获得了多数派的承认。可是我这里想说的是,上述流程在绝大部份场景下没问题,听上去也很是合理,可是倒是有bug的。

由于上述流程并无限制在选举过程当中,一个Node只能投一票,那么什么场景下会投两票呢?好比Node_B投Node_A一票,可是Node_A迟迟不成为Master,Node_B等不及了发起了下一轮选主,这时候发现集群里多了个Node_0,Node_0优先级比Node_A还高,那Node_B确定就改投Node_0了。假设Node_0和Node_A都处在等选票的环节,那显然这时候Node_B其实发挥了两票的做用,并且投给了不一样的人。

那么这种问题应该怎么解决呢,好比raft算法中就引入了选举周期(term)的概念,保证了每一个选举周期中每一个成员只能投一票,若是须要再投就会进入下一个选举周期,term+1。假如最后出现两个节点都认为本身是master,那么确定有一个term要大于另外一个的term,并且由于两个term都收集到了多数派的选票,因此多数节点的term是较大的那个,保证了term小的master不可能commit任何状态变动(commit须要多数派节点先持久化日志成功,因为有term检测,不可能达到多数派持久化条件)。这就保证了集群的状态变动老是一致的。

而ES目前(6.2版本)并无解决这个问题,构造相似场景的测试case能够看到会选出两个master,两个node都认为本身是master,向全集群发布状态变动,这个发布也是两阶段的,先保证多数派节点“接受”此次变动,而后再要求所有节点commit此次变动。很不幸,目前两个master可能都完成第一个阶段,进入commit阶段,致使节点间状态出现不一致,而在raft中这是不可能的。那么为何都能完成第一个阶段呢,由于第一个阶段ES只是将新的cluster_state作简单的检查后放入内存队列,若是当前cluster_state的master为空,不会对新的cluster_state中的master作检查,即在接受了Node_A成为master的cluster_state后(还未commit),还能够继续接受Node_B成为cluster_state。这就使Node_A和Node_B都能达到commit条件,发起commit命令,从而将集群状态引向不一致。固然,这种脑裂很快会自动恢复,由于不一致发生后某个master再次发布cluster_state时就会发现没法达到多数派条件,或者是发现它的follower并不构成多数派而自动降级为candidate等。

这里要表达的是,ES的ZenDiscovery模块与成熟的一致性方案相比,在某些特殊场景下存在缺陷,下面讲ES的meta变动流程时也会分析其余的ES没法知足一致性的场景。

错误检测

  1. MasterFaultDetection与NodesFaultDetection

这里的错误检测能够理解为相似心跳的机制,有两类错误检测,一类是Master按期检测集群内其余的Node,另外一类是集群内其余的Node按期检测当前集群的Master。检查的方法就是按期执行ping请求。ES文档:

There are two fault detection processes running. The first is by the master, to ping all the other nodes in the cluster and verify that they are alive. And on the other end, each node pings to master to verify if its still alive or an election process needs to be initiated.

若是Master检测到某个Node连不上了,会执行removeNode的操做,将节点从cluster_state中移除,并发布新的cluster_state。当各个模块apply新的cluster_state时,就会执行一些恢复操做,好比选择新的primaryShard或者replica,执行数据复制等。

若是某个Node发现Master连不上了,会清空pending在内存中还未commit的new cluster_state,而后发起rejoin,从新加入集群(若是达到选举条件则触发新master选举)。

2. rejoin
除了上述两种状况,还有一种状况是Master发现本身已经不知足多数派条件(>=minimumMasterNodes)了,须要主动退出master状态(退出master状态并执行rejoin)以免脑裂的发生,那么master如何发现本身须要rejoin呢?

上面提到,当有节点连不上时,会执行removeNode。在执行removeNode时判断剩余的Node是否知足多数派条件,若是不知足,则执行rejoin。

if (electMasterService.hasEnoughMasterNodes(remainingNodesClusterState.nodes()) == false) {
                final int masterNodes = electMasterService.countMasterNodes(remainingNodesClusterState.nodes());
                rejoin.accept(LoggerMessageFormat.format("not enough master nodes (has [{}], but needed [{}])",
                                                         masterNodes, electMasterService.minimumMasterNodes()));
                return resultBuilder.build(currentState);
            } else {
                return resultBuilder.build(allocationService.deassociateDeadNodes(remainingNodesClusterState, true, describeTasks(tasks)));
            }

在publish新的cluster_state时,分为send阶段和commit阶段,send阶段要求多数派必须成功,而后再进行commit。若是在send阶段没有实现多数派返回成功,那么多是有了新的master或者是没法链接到多数派个节点等,则master须要执行rejoin。

try {
            publishClusterState.publish(clusterChangedEvent, electMaster.minimumMasterNodes(), ackListener);
        } catch (FailedToCommitClusterStateException t) {
            // cluster service logs a WARN message
            logger.debug("failed to publish cluster state version [{}](not enough nodes acknowledged, min master nodes [{}])",
                newState.version(), electMaster.minimumMasterNodes());

            synchronized (stateMutex) {
                pendingStatesQueue.failAllStatesAndClear(
                    new ElasticsearchException("failed to publish cluster state"));

                rejoin("zen-disco-failed-to-publish");
            }
            throw t;
        }

在对其余节点进行按期的ping时,发现有其余节点也是master,此时会比较本节点与另外一个master节点的cluster_state的version,谁的version大谁成为master,version小的执行rejoin。

if (otherClusterStateVersion > localClusterState.version()) {
            rejoin("zen-disco-discovered another master with a new cluster_state [" + otherMaster + "][" + reason + "]");
        } else {
            // TODO: do this outside mutex
            logger.warn("discovered [{}] which is also master but with an older cluster_state, telling [{}] to rejoin the cluster ([{}])", otherMaster, otherMaster, reason);
            try {
                // make sure we're connected to this node (connect to node does nothing if we're already connected)
                // since the network connections are asymmetric, it may be that we received a state but have disconnected from the node
                // in the past (after a master failure, for example)
                transportService.connectToNode(otherMaster);
                transportService.sendRequest(otherMaster, DISCOVERY_REJOIN_ACTION_NAME, new RejoinClusterRequest(localClusterState.nodes().getLocalNodeId()), new EmptyTransportResponseHandler(ThreadPool.Names.SAME) {

                    @Override
                    public void handleException(TransportException exp) {
                        logger.warn((Supplier<?>) () -> new ParameterizedMessage("failed to send rejoin request to [{}]", otherMaster), exp);
                    }
                });
            } catch (Exception e) {
                logger.warn((Supplier<?>) () -> new ParameterizedMessage("failed to send rejoin request to [{}]", otherMaster), e);
            }
        }

集群扩缩容
上面讲了节点发现、Master选举、错误检测等机制,那么如今咱们能够来看一下如何对集群进行扩缩容。

1 扩容DataNode
假设一个ES集群存储或者计算资源不够了,咱们须要进行扩容,这里咱们只针对DataNode,即配置为:

conf/elasticsearch.yml:

node.master: false
node.data: true

而后须要配置集群名、节点名等其余配置,为了让该节点可以加入集群,咱们把discovery.zen.ping.unicast.hosts配置为集群中的master-eligible node。

conf/elasticsearch.yml:

cluster.name: es-cluster
node.name: node_Z
discovery.zen.ping.unicast.hosts: ["x.x.x.x", "x.x.x.y", "x.x.x.z"]

而后启动节点,节点会自动加入到集群中,集群会自动进行rebalance,或者经过reroute api进行手动操做。

https://www.elastic.co/guide/...

https://www.elastic.co/guide/...

2 缩容DataNode
假设一个ES集群使用的机器数太多了,须要缩容,咱们怎么安全的操做来保证数据安全,而且不影响可用性呢?

首先,咱们选择须要缩容的节点,注意本节只针对DataNode的缩容,MasterNode缩容涉及到更复杂的问题,下面再讲。

而后,咱们须要把这个Node上的Shards迁移到其余节点上,方法是先设置allocation规则,禁止分配Shard到要缩容的机器上,而后让集群进行rebalance。

PUT _cluster/settings
{
"transient" : {

"cluster.routing.allocation.exclude._ip" : "10.0.0.1"

}
}
等这个节点上的数据所有迁移完成后,节点能够安全下线。

更详细的操做方式能够参考官方文档:

https://www.elastic.co/guide/...

3 扩容MasterNode
假如咱们想扩容一个MasterNode(master-eligible node), 那么有个须要考虑的问题是,上面提到为了不脑裂,ES是采用多数派的策略,须要配置一个quorum数:

conf/elasticsearch.yml:

discovery.zen.minimum_master_nodes: 2

假设以前3个master-eligible node,咱们能够配置quorum为2,若是扩容到4个master-eligible node,那么quorum就要提升到3。

因此咱们应该先把discovery.zen.minimum_master_nodes这个配置改为3,再扩容master,更改这个配置能够经过API的方式:

curl -XPUT localhost:9200/_cluster/settings -d '{

"persistent" : {
    "discovery.zen.minimum_master_nodes" : 3
}

}'
这个API发送给当前集群的master,而后新的值当即生效,而后master会把这个配置持久化到cluster meta中,以后全部节点都会以这个配置为准。

可是这种方式有个问题在于,配置文件中配置的值和cluster meta中的值极可能出现不一致,不一致很容易致使一些奇怪的问题,好比说集群重启后,在恢复cluster meta前就须要进行master选举,此时只可能拿配置中的值,拿不到cluster meta中的值,可是cluster meta恢复后,又须要以cluster meta中的值为准,这中间确定存在一些正确性相关的边界case。

总之,动master节点以及相关的配置必定要谨慎,master配置错误颇有可能致使脑裂甚至数据写坏、数据丢失等场景。

4 缩容MasterNode
缩容MasterNode与扩容跟扩容是相反的流程,咱们须要先把节点缩下来,再把quorum数调下来,再也不详细描述。

与Zookeeper、raft等实现方式的比较

  1. 与使用Zookeeper相比

本篇讲了ES集群中节点相关的几大功能的实现方式:

节点发现
Master选举
错误检测
集群扩缩容
试想下,若是咱们使用Zookeeper来实现这几个功能,会带来哪些变化?

Zookeeper介绍
咱们首先介绍一下Zookeeper,熟悉的同窗能够略过。

Zookeeper分布式服务框架是Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中常常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。

简单来讲,Zookeeper就是用于管理分布式系统中的节点、配置、状态,并完成各个节点间进行配置和状态的同步等。大量的分布式系统依赖于Zookeeper或者是相似的组件。

Zookeeper经过目录树的形式来管理数据,每一个节点称为一个znode,每一个znode由3部分组成:

此为状态信息, 描述该znode的版本, 权限等信息.
与该znode关联的数据.
该znode下的子节点.
stat中有一项是ephemeralOwner,若是有值,表明是一个临时节点,临时节点会在session结束后删除,能够用来辅助应用进行master选举和错误检测。

Zookeeper提供watch功能,能够用于监听相应的事件,好比某个znode下的子节点的增减,某个znode自己的增减,某个znode的更新等。

怎么使用Zookeeper实现ES的上述功能

  1. 节点发现:每一个节点的配置文件中配置一下Zookeeper服务器的地址,节点启动后到Zookeeper中某个目录中注册一个临时的znode。当前集群的master监听这个目录的子节点增减的事件,当发现有新节点时,将新节点加入集群。
  2. master选举:当一个master-eligible
    node启动时,都尝试到固定位置注册一个名为master的临时znode,若是注册成功,即成为master,若是注册失败则监听这个znode的变化。当master出现故障时,因为是临时znode,会自动删除,这时集群中其余的master-eligible
    node就会尝试再次注册。使用Zookeeper后实际上是把选master变成了抢master。
  3. 错误检测:因为节点的znode和master的znode都是临时znode,若是节点故障,会与Zookeeper断开session,znode自动删除。集群的master只须要监听znode变动事件便可,若是master故障,其余的候选master则会监听到master
    znode被删除的事件,尝试成为新的master。

    集群扩缩容:扩缩容将再也不须要考虑minimum_master_nodes配置的问题,会变得更容易。

使用Zookeeper的优劣点

使用Zookeeper的好处是,把一些复杂的分布式一致性问题交给Zookeeper来作,ES自己的逻辑就能够简化不少,正确性也有保证,这也是大部分分布式系统实践过的路子。而ES的这套ZenDiscovery机制经历过不少次bug fix,到目前仍有一些边角的场景存在bug,并且运维也不简单。

那为何ES不使用Zookeeper呢,大概是官方开发以为增长Zookeeper依赖后会多依赖一个组件,使集群部署变得更复杂,用户在运维时须要多运维一个Zookeeper。

那么在自主实现这条路上,还有什么别的算法选择吗?固然有的,好比raft。

2. 与使用raft相比
raft算法是近几年很火的一个分布式一致性算法,其实现相比paxos简单,在各类分布式系统中也获得了应用。这里再也不描述其算法的细节,咱们单从master选举算法角度,比较一下raft与ES目前选举算法的异同点:

相同点
多数派原则:必须获得超过半数的选票才能成为master。
选出的leader必定拥有最新已提交数据:在raft中,数据更新的节点不会给数据旧的节点投选票,而当选须要多数派的选票,则当选人必定有最新已提交数据。在es中,version大的节点排序优先级高,一样用于保证这一点。
不一样点
正确性论证:raft是一个被论证过正确性的算法,而ES的算法是一个没有通过论证的算法,只能在实践中发现问题,作bug fix,这是我认为最大的不一样。
是否有选举周期term:raft引入了选举周期的概念,每轮选举term加1,保证了在同一个term下每一个参与人只能投1票。ES在选举时没有term的概念,不能保证每轮每一个节点只投一票。
选举的倾向性:raft中只要一个节点拥有最新的已提交的数据,则有机会选举成为master。在ES中,version相同时会按照NodeId排序,老是NodeId小的人优先级高。

见解

raft从正确性上看确定是更好的选择,而ES的选举算法通过几回bug fix也愈来愈像raft。固然,在ES最先开发时尚未raft,而将来ES若是继续沿着这个方向走极可能最终就变成一个raft实现。

raft不只仅是选举,下一篇介绍meta数据一致性时也会继续比较ES目前的实现与raft的异同。

小结

本篇介绍了Elasticsearch集群的组成、节点发现、master选举、故障检测和扩缩容等方面的实现,与通常的文章不一样,本文对其原理、存在的问题也进行了一些分析,并与其余实现方式进行了比较。

做为Elasticsearch分布式一致性原理剖析系列的第一篇,本文先从节点入手,下一篇会介绍meta数据变动的一致性问题,会在本文的基础上对ES的分布式原理作进一步分析。
详情请阅读原文

相关文章
相关标签/搜索