前言
分布式,很多初学者对这个词的第一印象——高大上技术范儿。抛开技术细节不谈,纵观后台技术的发展,存在着普遍适用的规律,一项新技术的诞生,总是解决一些现有架构无法解决的问题。如果读者凭空去学习分布式,便容易坠入云里雾里。本文作为笔者自己学习的一个梳理,以实际问题出发阐述了笔者对Raft
协议的理解。本文并不对Raft
协议的实现机制做详细的描述,只是从一个新手解决问题的角度去阐述Raft
协议做了些什么,不正确的地方请读者指正(邮箱:dreamcatchwang1991@gmail.com)。
思考
以经典单数据库实例架构(这也是很多企业级应用的典型架构)为例,所有的业务数据均存储于单机数据库,当数据库实例Crash
了以后,业务便受到影响,在大多数情况下,这种Crash
对企业业务的影响是可控范的。然而在互联网应用中,哪怕是一分钟的Crash
对企业来说也是致命的,比如前段时间,美团的外卖系统出现崩溃,整个服务停摆几个小时,造成大量用户流失到饿了么平台。
笔者尝试根据自己的经验去解决该问题,为了让单机数据库实例在Crash
了以后,整个系统仍然保持可用,我们很容易想到的一个策略——冗余(比如你在单位请假了需要有人代替你继续工作而不影响业务)。我们增加了一台数据库实例B
(原来的数据库实例用A
表示),在实例A
挂掉了之后,我们期望B
可以代替A
继续提供服务,*所以B
与A
必须具备一样的数据**,在分布式里面这个称作一致性*。Raft
协议为**分布式一致性协议的一种实现,主要目标就是解决上述这类问题。
脱离现有的MySQL
,Redis
,Kafka
等高可用方案(因为这些系统为了性能而做出一些折中),我们根据自己的诉求,去设计一个高可用的存储系统,需要注意哪些问题呢?假设我们的存储系统有A
,B
,C
等3个节点用来保持高可用,那我们该怎么保持A
,B
,C
3个节点内数据的一致性呢?
- 一致性由客户端保证还是服务端保证
- 如何保证
A,B,C
或更多节点的数据一致性
首先分析第一个问题,假设一致性工作是由客户端保证的(客户端向A
写入数据的同时向B
和C
写入数据,为保证A,B,C
的一致性,需A,B,C
3个节点全部写入成功,客户端回才判定写入成功),我们可能会遇到如下情况:
B
下线了一段时间又重新上线,因为客户端未保存B
处于下线状态这段时间的数据,所以B
中就会缺失这部分数据,因而B
中数据会与A
与C
中数据不一致。- 客户端向
A
与C
中写入数据成功,但向B
中写入数据失败,这次写入应当被认定为失败(因为A
,B
,C
中数据不一致,也无法通过其他途径达到一致),我们期望整个系统可以表现的犹如一个**事务**,要么全部成功,要么全部失败回滚修改,客户端无法提供这种机制。
综上,**由客户端保证数据的一致性是不可取的**。
我们将一致性保证工作放在服务端实现,那么我们如何保证A,B,C
三节点数据的一致性呢?首先我们思考一个问题,**我们无法预知A,B,C
三个节点中哪个节点会意外挂掉,所以客户端不应该至同单一节点建立联系**,也就是说——A,B,C
3个节点对外应当表现为一个整体,也就是集群Cluster
。那么客户端该如何向A,B,C
组成的集群写入数据?以下是笔者想到的实现方式:
- 所有客户端均向
A,B,C
中某一节点(比如A
)写入数据,由该节点将数据拷贝至其它节点以达到一致性。 - 向建立连接的节点写入数据,比如
客户端1
同A
建立连接,客户端1
向A
写入数据,客户端2
同B
建立连接,客户端2
向B
写入数据,以此类推。
读者是否觉得以上两种实现方式似曾相识——这和*并发编程**下的并发更改共享变量问题相似,由经验我们可知,我们最好是将对共享的操作串行,有序的***执行。同样,如果多个客户端通过多个节点向集群写入数据,为了达到每个节点都有一份完整数据的目的,多个节点间会进行通讯,数据合并,而这其中又牵扯了数据的顺序等许多问题,工程实现起来比较复杂。
当然不是说不可以,笔者没见过这么做的~ ~)
方式一为目前流行的一致性解决思路,Raft
协议采用了该思路,Raft
协议解决了方式一面临的两大问题:
- 集群启动(或者写入节点下线)时,如何选举出一个节点作为写入节点
- 写入节点如何与其它节点通讯,复制数据,保持数据在各节点的一致性
以上两大问题便是Raft
协议的两大功能:
Leader Election
Log Replication
分布式中任何环节都是不可靠的,实际问题比本人论述的复杂的多,但明确了上述问题,再去研究Raft Paper
时,读者便可以快速掌握Raft
协议。
建议大家观看Raft
协议动画,简单明了生动:http://thesecretlivesofdata.com/raft/
参考
[1] Raft Pager