Open AlexiaChen opened 4 years ago
一般本质上都是通过将数据复制到多个存储设备,通过数据冗余的方式来实现高可用,其复杂性主要体现在如何应对网络延迟和网络中断导致的数据不一致的问题。
所以思考的问题是:
最常见的方案,也是最简单的。几乎所有存储系统都提供了主备复制的功能,比如MySQL,Redis,MongoDB等。
主机存储数据,通过复制通道将数据复制到备机
正常情况下,客户端无论读写操作,都发送给主机,备机不对外提供任何读写服务
主机故障情况下,客户端也不会自动将请求发给备机,此时,整个系统处于不可用状态,不能读写数据,但是数据没有全部丢失,因为备机上有数据。
如果主机能够恢复(无论人工恢复还是自动恢复),客户端继续访问主机,主机继续将数据复制给备机。
如果主机短时间不能恢复,则需要人工干预,将备机提升为主机,然后客户端访问新的主机。同时,为了继续保持主备架构,需要人工增加新的机器作为备机。
主机不能恢复的情况下,成功写如了主机的数据但还没有来得及复制到备机,那么数据会有丢失风险,需要人工进行排查和恢复,也许数据永远不在了,业务上需要考虑如何应对此风险
如果主备复制有延迟,由于备机不对外提供服务,因此对业务没啥影响,但是如果延迟较多,恰好此时主机挂了,则可能丢失较多数据,因此对复制延迟也不能掉以轻心。一般做法是做复制延迟的监控措施,当延迟数据量较大时,及时报警,人工干预。
优点:
客户端无需感知备机存在,灾难恢复后,也只是把备机提升为了主机,客户端照样读写
主备关注的只是双机备份复制,无须进行状态判断,主备切换这类复杂的操作
缺点:
备机仅仅是备份,没有提供读写服务,硬件成本上有浪费
故障后需要人工干预,无法自动恢复,不符合现代互联网主流手法
一般这种方案不会用在互联网中,但是用在企业管理后台就挺多了,有些企业后台数据不是很重要,比如某学校或公司的员工管理系统,考勤系统等等。业务量不大,数据变更频率低。可以用主备复制方案。
与主备只差一个字,“从”是slave,随从,奴仆的意思,意思从机是要承担一部分主机的工作量的,不仅仅作为备份,它要干活的。一般从机只承担读操作,主机负责读写操作,一般业务的读写分离,就是这样的架构。
主机存储数据,通过复制通道将数据复制到从机
正常情况下,客户端写操作发送给主机,读操作可以发送给主机也可以发送给从机,可以随机读,也可以轮询读,也可以只读主机。根据业务特点选择即可。
主机故障情况下,客户端无法进行写操作,但可以将读操作发送给从机,从机继续响应读操作,此时和写业务相关的操作不可用,但和读操作相关的业务不影响。
主机如果能够恢复(无论人工恢复还是自动恢复),客户端继续将写请求发送给主机,主机继续将数据复制给从机。
主机不能恢复的情况下,成功写如了主机的数据但还没有来得及复制到备机,那么数据会有丢失风险,需要人工进行排查和恢复,也许数据永远不在了,业务上需要考虑如何应对此风险.(与主备方案一样)
如果主从数据复制延迟,则会出现主从读取的数据不一致。这样一般可以先读取从机,读取不到的时候,再从主机那里去读
如果主从复制延迟较多,恰好此时主机挂了,则可能丢失较多数据,因此对复制延迟也不能掉以轻心。一般做法是做复制延迟的监控措施,当延迟数据量较大时,及时报警,人工干预。(与主备方案一样)
主机故障时,读操作相关的业务不受影响
从机是要干活的,发挥了硬件优势,不仅仅是备份作用
整体复杂度高些,因为客户端需要感知主从关系,并将读写操作发给不同的机器进行处理。
主机故障时,需要人工干预,效率低,人工操作更容易出错
一般这种方案不会用在互联网行业的主流业务中,因为互联网行业要7 × 24小时运行,更不可能需要人工干预。但是这样的方案,适合用在论坛,BBS,新闻网站这类读多写少的业务中,此类业务的读操作一般在写操作的10倍乃至百倍以上。如果搭建一个社区,学校BBS啥的,确实可以考虑主从。
主备和主从有两个共性问题:
主机故障后,都无法进行写业务
如果主机无法恢复,需要人工干预,指定新的主机角色
主备切换和主从切换其实就是增加了一个自动切换角色的功能,无需人工干预,以达到高可用。貌似就是自动化而已,没什么难的? 其实不是的,自动切换复杂度提升了一个量级。如果选择自研,以代码量来衡量就是10倍的差距,差了一个数量级。为什么会这么复杂? 接着往下看:
切换时机: 什么情况下应该将从机提升为主机?是机器掉电后从机才升级,还是主机上的进程不存在就升级,还是主机响应时间超过2s就升级,还是3分钟内主机连续重启3次就升级等等
切换策略:原来的主机故障恢复后,要再次切换,确保原来的主机继续做主机,还是原来的主机故障恢复后自动成为新的从机
自动程度: 切换是完全自动,无需人工干预,还是半自动,要人工参与部分决策?
当故障的主机恢复后,主从之间可能存在数据冲突,冲突的时候该怎么选择?你用git的时候不同的分支数据合并的时候,都会冲突呢,到底用谁的数据?
主从切换不同的存储系统其实已经有成熟的方案了,MySQL有MHA,Redis有Sentinel。所以小公司,没有研发能力的,还是尽量用成熟的方案吧。当然,还可以选择部分自研,你可以用Zookeeper来作为第三方仲裁协调节点,来让MySQL达到高可用。你在网上可以搜到基于Zookeeper来实现MySQL HA的方案。
这样的主从自动切换方案,就开始接近现代互联网的存储高可用方案了。
意思就是两台机器都是主机,互相将数据复制给对方,客户端可以任意选择其中一个机器进行读写业务。
两台主机存储数据,互相通过复制通道复制数据给另一台机器。
正常情况下,客户端可以将读写操作发送给任意一台机器。
一台主机故障情况下,例如主机A挂了,客户端只需要将读写操作发送给主机B即可。
如果故障的主机A恢复(无论人工还是自动恢复),客户端继续访问两台主机其中一个,两台主机互相复制数据给对方
如果故障主机A不能恢复,则需要人工介入,增加一台新的机器C作为新主机
原有故障主机A不能恢复的情况下,成功写入了原有故障主机,但还没有复制到正常主机B的数据会丢失,需要人工介入排查,也许有的数据就永远丢了,需要业务上考虑如何应对此风险。
如果两台主机间复制延迟,则可能出现客户端刚写入了数据到主机A,然后到主机B去读取,此时读取不到刚刚写入的数据。
不存在主从切换,因为两台都是主机
客户端无需区分,随便将读写操作发送给任意一台主机都可以
主主复制架构必须保证数据能够双向复制,但是,很多数据是不能双向复制的。比如,用户ID自增,如果双向复制,会导致不一致,冲突。余额这样的数据双向复制也会出问题。
所以,主主复制架构对数据的设计有严格要求,一般适合临时的,可丢失的,可覆盖的数据场景。比如,用户登录的session数据,用户行为的日志数据,论坛的草稿数据。
主备,主从,主主架构都有一个隐含的假设:就是,主机能够存储所有数据(并且都是两台机器)。然而,对于现代互联网企业,没有这样的存储能力和处理能力应对海量数据的主机。2013年的时候,FaceBook一天上传的图片差不多就是3亿5000万张。到现在差不多也是千亿级别的图片数量,照片的容量已经达到PB级别,这么大的数据量肯定是需要数据集群架构了。
集群分为两大类,数据集中式和数据分散式集群。
这种架构与主备,主从架构类似,它无非就是把备机,从机弄成多个,叫1主多备,一主多从。一般读写业务也只是到主机,读业务可以分散都各种备机,复制通道不止一条,可能也会导致备机之间数据不一致。解决方案也可以用Zookeeper来协调。对于现代互联网企业,这种架构也是用不了了。暂时不过多讨论。
意思就是,多个服务器组成一个集群,每台服务器负责存储一部分数据(Region)。为了提升可用性,同时每台服务器又会备份一部分数据(Replica)。
一般这样的架构就已经是现代互联网的主流思路了。复杂点在于如何将数据分散到不同的服务器上,需要考虑以下三点:
均匀性,保证每台服务器上的数据分区(Region)基本是均匀的,不能存在某台服务器上的Region数量是另一台服务器的几倍的情况。
容错性,当出现部分服务器故障时,算法需要将原来分配给故障服务器的数据分区(Region)分配给其他服务器。
可伸缩性,当集群容量不够,扩充新服务器后,算法能自动将部分数据分区迁移到新服务器,并保证扩容后所有服务器的均匀性。
在分散式集群中,每台服务器都可以处理读写请求,与集中式不一样。在分散式集群中,必须有一个角色来负责执行数据分配算法,这个角色可以是一台独立配置的服务器(会有单点故障风险),也可以是集群自己选举出的一台服务器(通过分布式共识算法,无单点故障)。一般选举出来的节点机器叫主机,也可以叫leader或者manager节点。
Hadoop的实现就是独立配置的服务器负责数据分区的分配,这台服务器叫Namenode。这台服务器是中心化的节点,负责管理一堆Datanode。Namenode执行文件系统的名字空间操作,也负责确定文件数据块到具体的Datanode节点的映射。Datanode处理文件系统客户端的读写请求,在Namenode的统一调度下进行数据块的创建,删除和复制操作。
ElasticSerach集群就与Hadoop集群不一样了,它是由分布式共识算法选举出来的leader节点来做数据分区的分配,叫master node。
所以,你也看到了,就技术先进性来说,肯定是分散式架构更好,分散式也更符合主流互联网企业大数据的诉求,也决定了两种架构的应用场景不同。集中式显然适合数据量不大,集群机器数量不多的场景。而分散式集群由于良好的可伸缩性,适合的业务数据就是海量数据,机器数量庞大,比如Hadoop,HBase集群,节点规模可以达到上百,上千。
某些业务场景需要事务来保证数据一致性,如果采用了数据集群方案,那么数据可能分散在不同的节点上,由于节点间只能通过消息进行通信,因此分布式事务实现起来只能依赖消息通知。但是由于网络本身不可靠,消息可能会丢失,这就给分布式事务的实现带来了复杂性。
分布式事务也要保证ACID,比较有名的算法是两阶段提交协议(2PC)和三阶段提交协议(3PC)。
字面意思,就是由两个阶段步骤组成,分别是commit-request阶段(投票阶段)和commit阶段。该算法的成立基于以下假设:
在分布式系统中,存在一个节点作为协调者(coordinator),其他节点作为参与者(participants,cohorts或workers),且节点之间可以进行网络通信。
所有节点都采用预写日志(Write-ahead Log,WAL),且日志被写入后即保持在可靠的存储设备上,即使节点损坏,也不会导致日志数据丢失。
所有节点不会永久性损坏,即使损坏,仍然可以恢复。
所以从以上假设上看,最好每个节点都需要支持单机ACID本地事务。
协调者向所有参与者发送QUERY TO COMMIT消息,询问是否可以执行提交事务,并开始等待各参与者的响应。
参与者执行询问发起为止的所有事务操作(从begin到commit),并将Undo信息和Redo信息写入日志,返回Yes消息给协调者;如果参与者执行失败,则返回No消息给协调者。(主要是Write-ahead Log)
有时候这一阶段也被成为投票阶段,即各参与者投票是否继续接下来的提交操作。
当所有参与者都返回Yes时(成功):
当所有参与者其中任意一个返回No时,或者投票阶段由于网络超时导致接收响应失败(失败):
这个阶段有时候叫完成阶段,因为无论结果怎样,协调者都必须在此阶段结束当前事务
2PC是强一致性事务算法,优点是实现简单,但是缺点也很明显:
同步阻塞:整个算法的执行过程中,协调者与参与者互相等待对方的响应消息,等待过程中节点处于阻塞状态,不能做其他事情,如果某个节点响应消息比较慢,则整个系统全部被拖慢,导致2PC存在明显性能问题,难以支撑高并发应用场景
状态不一致: 在第二阶段执行过程中,如果协调者发出COMMIT请求,但是某个参与者节点并没有收到这个消息,其他参与者收到了这个消息,那么收到消息的参与者会提交事务,未收到消息的参与者超时会回滚事务,导致参与者节点之间数据状态不一致。虽然协调者在这种情况下可以再发送ROLLBACK消息给各个参与者,但是这条ROLLBACK消息一样存在丢失问题,所以极端情况下,无论怎样处理,都可能出现数据状态不一致的情况。
单点故障: 协调者是整个算法的单点,如果协调者故障,则参与者会一直阻塞下去,导致整个数据库集群将不可用。(无法提供服务)
3PC从名字上看就像为了解决一部分2PC带来的问题而生的,事实上也确实是这样的,3PC是针对2PC存在的“单点故障”和“可能一直阻塞”而提出的解决方案。通过在2PC的两个阶段之间插入一个新的中间阶段----“准备阶段”,当协调者故障后,参与者可以通过超时提交来避免一直阻塞。
也叫提交判断阶段
也叫准备提交阶段
也叫提交执行阶段
优点提到了,只说缺点,缺点就是在极端情况下还是会存在数据不一致的问题。
业界XA协议,TCC(Try-Confirm-Cancel)这种柔性事务方案。
有些时候也叫分布式一致性算法。分布式事务算法主要目的是为了保证分散在多个节点上的数据的统一提交或统一回滚,以满足分布式事务的ACID的要求。而分布式共识算法的主要目的是为了多个节点达成一致性的共识,这样就可以做到保证同一份数据在多个节点上的一致性,以满足CAP中的CP要求。
复制状态机的核心就是分布式共识算法,该算法很复杂,其中特别死Paxos最难,最复杂。
Lamport老爷子发明,最有名的就是这个算法了,已经被理论上证明是正确的算法了。
Paxos的一个问题就是,特别复杂,特别难以理解,Raft的论文中都提到,如果不是作者读了很多资料,并且自己设计了代替的算法之后是不能够真正理解Paxos算法的。Raft作者这么牛逼的人物都觉得Paxos复杂了,可见还是复杂的。
Paxos还有一个巨大的问题就是,论文中缺乏很多细节,工程上难以实现,这些细节在工程实现上不可避免。 有些Paxos算法的工程实现,可能会有bug,也不能被证明正确。
Google的分布式协调服务Chubby底层就是一个以Paxos为基础的一致性算法,但是其开发者这么评论过:Paxos算法的描述和真实世界的系统的需求实现存在巨大的实现鸿沟,最终系统只能实现为一个没有被证明为正确的Paxos-like的共识协议。
虽然如此,但Paxos算法的地位是不可代替的,Chubby的作者也评价过:世界上只有一种共识算法,就是Paxos,所有其他一致性算法都是Paxos算法的阉割版。
这个章节能力有限,就不讲Paxos算法描述了,不过Paxos如此有名,甚至Paxos的原始论文都有翻译质量比较高的中文翻译了,还有精读注解。
为了解决复杂不堪,难以理解,工程上难以实现正确的Paxos算法,由此Raft算法诞生了。之前提到过,非Paxos算法的共识算法都是Paxos的阉割版。恩,Raft算法从理论上来讲确实不是一个完备的分布式共识算法。
为了可理解,可操作,可实践,Raft对一些复杂的处理做了一些简化,以保证绝大部分情况下都保证一致性。
Raft算法通过将分布式一致性的问题拆分为3个子问题来简化算法:Leader选举,日志复制,安全保证。Raft强化了leader的作用,通过leader来保证分布式一致性。Raft算法也详细说明了具体工程实现时的各种方案细节。比如,日志亚索,Client互交等。基本上对照算法论文就能够实现,而且代码量也不大。
Raft论文中提到只用了2000行C++代码就实现了算法。
全称:Zookeeper Atomic Broadcast Protocol,是Zookeeper系统中采用的分布式共识算法。抛开各种细节,ZAB和Raft的实现类似,也是Paxos的阉割版。比如,强化leader的作用,通过leader来保证分布式一致性。
ZAB,Paxos和Raft有一个较大的差异就是复制的方式,Paxos和Raft采用的是状态机副本(state machine replication,又叫active replication),ZAB采用的是主备份(primary backup,又称passive replication)。
这样的思路有点像MySQL中的binlog,确实有点像。
注意:以上的共识算法的前提都是非拜占庭网络环境(一般是公司内网,不考虑存在恶意节点),如果是拜占庭网络环境(公网,一定要考虑恶意节点作恶),那么共识算法就不是这些了,拜占庭网络的共识算法一般是区块链相关的共识算法:PoW,PoS,DPoS,PBFT等。
EOF
存储高可用
一般本质上都是通过将数据复制到多个存储设备,通过数据冗余的方式来实现高可用,其复杂性主要体现在如何应对网络延迟和网络中断导致的数据不一致的问题。
所以思考的问题是:
主备复制
最常见的方案,也是最简单的。几乎所有存储系统都提供了主备复制的功能,比如MySQL,Redis,MongoDB等。
基本实现
主机存储数据,通过复制通道将数据复制到备机
正常情况下,客户端无论读写操作,都发送给主机,备机不对外提供任何读写服务
主机故障情况下,客户端也不会自动将请求发给备机,此时,整个系统处于不可用状态,不能读写数据,但是数据没有全部丢失,因为备机上有数据。
如果主机能够恢复(无论人工恢复还是自动恢复),客户端继续访问主机,主机继续将数据复制给备机。
如果主机短时间不能恢复,则需要人工干预,将备机提升为主机,然后客户端访问新的主机。同时,为了继续保持主备架构,需要人工增加新的机器作为备机。
主机不能恢复的情况下,成功写如了主机的数据但还没有来得及复制到备机,那么数据会有丢失风险,需要人工进行排查和恢复,也许数据永远不在了,业务上需要考虑如何应对此风险
如果主备复制有延迟,由于备机不对外提供服务,因此对业务没啥影响,但是如果延迟较多,恰好此时主机挂了,则可能丢失较多数据,因此对复制延迟也不能掉以轻心。一般做法是做复制延迟的监控措施,当延迟数据量较大时,及时报警,人工干预。
优缺点分析
优点:
客户端无需感知备机存在,灾难恢复后,也只是把备机提升为了主机,客户端照样读写
主备关注的只是双机备份复制,无须进行状态判断,主备切换这类复杂的操作
缺点:
备机仅仅是备份,没有提供读写服务,硬件成本上有浪费
故障后需要人工干预,无法自动恢复,不符合现代互联网主流手法
一般这种方案不会用在互联网中,但是用在企业管理后台就挺多了,有些企业后台数据不是很重要,比如某学校或公司的员工管理系统,考勤系统等等。业务量不大,数据变更频率低。可以用主备复制方案。
主从复制
与主备只差一个字,“从”是slave,随从,奴仆的意思,意思从机是要承担一部分主机的工作量的,不仅仅作为备份,它要干活的。一般从机只承担读操作,主机负责读写操作,一般业务的读写分离,就是这样的架构。
基本实现
主机存储数据,通过复制通道将数据复制到从机
正常情况下,客户端写操作发送给主机,读操作可以发送给主机也可以发送给从机,可以随机读,也可以轮询读,也可以只读主机。根据业务特点选择即可。
主机故障情况下,客户端无法进行写操作,但可以将读操作发送给从机,从机继续响应读操作,此时和写业务相关的操作不可用,但和读操作相关的业务不影响。
主机如果能够恢复(无论人工恢复还是自动恢复),客户端继续将写请求发送给主机,主机继续将数据复制给从机。
主机不能恢复的情况下,成功写如了主机的数据但还没有来得及复制到备机,那么数据会有丢失风险,需要人工进行排查和恢复,也许数据永远不在了,业务上需要考虑如何应对此风险.(与主备方案一样)
如果主从数据复制延迟,则会出现主从读取的数据不一致。这样一般可以先读取从机,读取不到的时候,再从主机那里去读
如果主从复制延迟较多,恰好此时主机挂了,则可能丢失较多数据,因此对复制延迟也不能掉以轻心。一般做法是做复制延迟的监控措施,当延迟数据量较大时,及时报警,人工干预。(与主备方案一样)
优缺点分析
优点:
主机故障时,读操作相关的业务不受影响
从机是要干活的,发挥了硬件优势,不仅仅是备份作用
缺点:
整体复杂度高些,因为客户端需要感知主从关系,并将读写操作发给不同的机器进行处理。
主机故障时,需要人工干预,效率低,人工操作更容易出错
一般这种方案不会用在互联网行业的主流业务中,因为互联网行业要7 × 24小时运行,更不可能需要人工干预。但是这样的方案,适合用在论坛,BBS,新闻网站这类读多写少的业务中,此类业务的读操作一般在写操作的10倍乃至百倍以上。如果搭建一个社区,学校BBS啥的,确实可以考虑主从。
主备切换与主从切换
设计关键
主备和主从有两个共性问题:
主机故障后,都无法进行写业务
如果主机无法恢复,需要人工干预,指定新的主机角色
主备切换和主从切换其实就是增加了一个自动切换角色的功能,无需人工干预,以达到高可用。貌似就是自动化而已,没什么难的? 其实不是的,自动切换复杂度提升了一个量级。如果选择自研,以代码量来衡量就是10倍的差距,差了一个数量级。为什么会这么复杂? 接着往下看:
切换时机: 什么情况下应该将从机提升为主机?是机器掉电后从机才升级,还是主机上的进程不存在就升级,还是主机响应时间超过2s就升级,还是3分钟内主机连续重启3次就升级等等
切换策略:原来的主机故障恢复后,要再次切换,确保原来的主机继续做主机,还是原来的主机故障恢复后自动成为新的从机
自动程度: 切换是完全自动,无需人工干预,还是半自动,要人工参与部分决策?
当故障的主机恢复后,主从之间可能存在数据冲突,冲突的时候该怎么选择?你用git的时候不同的分支数据合并的时候,都会冲突呢,到底用谁的数据?
主从切换不同的存储系统其实已经有成熟的方案了,MySQL有MHA,Redis有Sentinel。所以小公司,没有研发能力的,还是尽量用成熟的方案吧。当然,还可以选择部分自研,你可以用Zookeeper来作为第三方仲裁协调节点,来让MySQL达到高可用。你在网上可以搜到基于Zookeeper来实现MySQL HA的方案。
这样的主从自动切换方案,就开始接近现代互联网的存储高可用方案了。
主主复制
意思就是两台机器都是主机,互相将数据复制给对方,客户端可以任意选择其中一个机器进行读写业务。
两台主机存储数据,互相通过复制通道复制数据给另一台机器。
正常情况下,客户端可以将读写操作发送给任意一台机器。
一台主机故障情况下,例如主机A挂了,客户端只需要将读写操作发送给主机B即可。
如果故障的主机A恢复(无论人工还是自动恢复),客户端继续访问两台主机其中一个,两台主机互相复制数据给对方
如果故障主机A不能恢复,则需要人工介入,增加一台新的机器C作为新主机
原有故障主机A不能恢复的情况下,成功写入了原有故障主机,但还没有复制到正常主机B的数据会丢失,需要人工介入排查,也许有的数据就永远丢了,需要业务上考虑如何应对此风险。
如果两台主机间复制延迟,则可能出现客户端刚写入了数据到主机A,然后到主机B去读取,此时读取不到刚刚写入的数据。
优点:
不存在主从切换,因为两台都是主机
客户端无需区分,随便将读写操作发送给任意一台主机都可以
缺点:
主主复制架构必须保证数据能够双向复制,但是,很多数据是不能双向复制的。比如,用户ID自增,如果双向复制,会导致不一致,冲突。余额这样的数据双向复制也会出问题。
所以,主主复制架构对数据的设计有严格要求,一般适合临时的,可丢失的,可覆盖的数据场景。比如,用户登录的session数据,用户行为的日志数据,论坛的草稿数据。
数据集群
主备,主从,主主架构都有一个隐含的假设:就是,主机能够存储所有数据(并且都是两台机器)。然而,对于现代互联网企业,没有这样的存储能力和处理能力应对海量数据的主机。2013年的时候,FaceBook一天上传的图片差不多就是3亿5000万张。到现在差不多也是千亿级别的图片数量,照片的容量已经达到PB级别,这么大的数据量肯定是需要数据集群架构了。
集群分为两大类,数据集中式和数据分散式集群。
数据集中式集群
这种架构与主备,主从架构类似,它无非就是把备机,从机弄成多个,叫1主多备,一主多从。一般读写业务也只是到主机,读业务可以分散都各种备机,复制通道不止一条,可能也会导致备机之间数据不一致。解决方案也可以用Zookeeper来协调。对于现代互联网企业,这种架构也是用不了了。暂时不过多讨论。
数据分散式集群
意思就是,多个服务器组成一个集群,每台服务器负责存储一部分数据(Region)。为了提升可用性,同时每台服务器又会备份一部分数据(Replica)。
一般这样的架构就已经是现代互联网的主流思路了。复杂点在于如何将数据分散到不同的服务器上,需要考虑以下三点:
均匀性,保证每台服务器上的数据分区(Region)基本是均匀的,不能存在某台服务器上的Region数量是另一台服务器的几倍的情况。
容错性,当出现部分服务器故障时,算法需要将原来分配给故障服务器的数据分区(Region)分配给其他服务器。
可伸缩性,当集群容量不够,扩充新服务器后,算法能自动将部分数据分区迁移到新服务器,并保证扩容后所有服务器的均匀性。
在分散式集群中,每台服务器都可以处理读写请求,与集中式不一样。在分散式集群中,必须有一个角色来负责执行数据分配算法,这个角色可以是一台独立配置的服务器(会有单点故障风险),也可以是集群自己选举出的一台服务器(通过分布式共识算法,无单点故障)。一般选举出来的节点机器叫主机,也可以叫leader或者manager节点。
Hadoop的实现就是独立配置的服务器负责数据分区的分配,这台服务器叫Namenode。这台服务器是中心化的节点,负责管理一堆Datanode。Namenode执行文件系统的名字空间操作,也负责确定文件数据块到具体的Datanode节点的映射。Datanode处理文件系统客户端的读写请求,在Namenode的统一调度下进行数据块的创建,删除和复制操作。
ElasticSerach集群就与Hadoop集群不一样了,它是由分布式共识算法选举出来的leader节点来做数据分区的分配,叫master node。
集中式与分散式架构总结
所以,你也看到了,就技术先进性来说,肯定是分散式架构更好,分散式也更符合主流互联网企业大数据的诉求,也决定了两种架构的应用场景不同。集中式显然适合数据量不大,集群机器数量不多的场景。而分散式集群由于良好的可伸缩性,适合的业务数据就是海量数据,机器数量庞大,比如Hadoop,HBase集群,节点规模可以达到上百,上千。
分布式事务算法
某些业务场景需要事务来保证数据一致性,如果采用了数据集群方案,那么数据可能分散在不同的节点上,由于节点间只能通过消息进行通信,因此分布式事务实现起来只能依赖消息通知。但是由于网络本身不可靠,消息可能会丢失,这就给分布式事务的实现带来了复杂性。
分布式事务也要保证ACID,比较有名的算法是两阶段提交协议(2PC)和三阶段提交协议(3PC)。
2PC
字面意思,就是由两个阶段步骤组成,分别是commit-request阶段(投票阶段)和commit阶段。该算法的成立基于以下假设:
在分布式系统中,存在一个节点作为协调者(coordinator),其他节点作为参与者(participants,cohorts或workers),且节点之间可以进行网络通信。
所有节点都采用预写日志(Write-ahead Log,WAL),且日志被写入后即保持在可靠的存储设备上,即使节点损坏,也不会导致日志数据丢失。
所有节点不会永久性损坏,即使损坏,仍然可以恢复。
所以从以上假设上看,最好每个节点都需要支持单机ACID本地事务。
2PC第一阶段
协调者向所有参与者发送QUERY TO COMMIT消息,询问是否可以执行提交事务,并开始等待各参与者的响应。
参与者执行询问发起为止的所有事务操作(从begin到commit),并将Undo信息和Redo信息写入日志,返回Yes消息给协调者;如果参与者执行失败,则返回No消息给协调者。(主要是Write-ahead Log)
有时候这一阶段也被成为投票阶段,即各参与者投票是否继续接下来的提交操作。
2PC第二阶段
当所有参与者都返回Yes时(成功):
当所有参与者其中任意一个返回No时,或者投票阶段由于网络超时导致接收响应失败(失败):
这个阶段有时候叫完成阶段,因为无论结果怎样,协调者都必须在此阶段结束当前事务
2PC优缺点
2PC是强一致性事务算法,优点是实现简单,但是缺点也很明显:
同步阻塞:整个算法的执行过程中,协调者与参与者互相等待对方的响应消息,等待过程中节点处于阻塞状态,不能做其他事情,如果某个节点响应消息比较慢,则整个系统全部被拖慢,导致2PC存在明显性能问题,难以支撑高并发应用场景
状态不一致: 在第二阶段执行过程中,如果协调者发出COMMIT请求,但是某个参与者节点并没有收到这个消息,其他参与者收到了这个消息,那么收到消息的参与者会提交事务,未收到消息的参与者超时会回滚事务,导致参与者节点之间数据状态不一致。虽然协调者在这种情况下可以再发送ROLLBACK消息给各个参与者,但是这条ROLLBACK消息一样存在丢失问题,所以极端情况下,无论怎样处理,都可能出现数据状态不一致的情况。
单点故障: 协调者是整个算法的单点,如果协调者故障,则参与者会一直阻塞下去,导致整个数据库集群将不可用。(无法提供服务)
3PC
3PC从名字上看就像为了解决一部分2PC带来的问题而生的,事实上也确实是这样的,3PC是针对2PC存在的“单点故障”和“可能一直阻塞”而提出的解决方案。通过在2PC的两个阶段之间插入一个新的中间阶段----“准备阶段”,当协调者故障后,参与者可以通过超时提交来避免一直阻塞。
3PC第一阶段
也叫提交判断阶段
3PC第二阶段
也叫准备提交阶段
3PC第三阶段
也叫提交执行阶段
3PC的优缺点
优点提到了,只说缺点,缺点就是在极端情况下还是会存在数据不一致的问题。
其他分布式事务算法
业界XA协议,TCC(Try-Confirm-Cancel)这种柔性事务方案。
分布式共识算法
有些时候也叫分布式一致性算法。分布式事务算法主要目的是为了保证分散在多个节点上的数据的统一提交或统一回滚,以满足分布式事务的ACID的要求。而分布式共识算法的主要目的是为了多个节点达成一致性的共识,这样就可以做到保证同一份数据在多个节点上的一致性,以满足CAP中的CP要求。
复制状态机的核心就是分布式共识算法,该算法很复杂,其中特别死Paxos最难,最复杂。
Paxos
Lamport老爷子发明,最有名的就是这个算法了,已经被理论上证明是正确的算法了。
Paxos的一个问题就是,特别复杂,特别难以理解,Raft的论文中都提到,如果不是作者读了很多资料,并且自己设计了代替的算法之后是不能够真正理解Paxos算法的。Raft作者这么牛逼的人物都觉得Paxos复杂了,可见还是复杂的。
Paxos还有一个巨大的问题就是,论文中缺乏很多细节,工程上难以实现,这些细节在工程实现上不可避免。 有些Paxos算法的工程实现,可能会有bug,也不能被证明正确。
Google的分布式协调服务Chubby底层就是一个以Paxos为基础的一致性算法,但是其开发者这么评论过:Paxos算法的描述和真实世界的系统的需求实现存在巨大的实现鸿沟,最终系统只能实现为一个没有被证明为正确的Paxos-like的共识协议。
虽然如此,但Paxos算法的地位是不可代替的,Chubby的作者也评价过:世界上只有一种共识算法,就是Paxos,所有其他一致性算法都是Paxos算法的阉割版。
这个章节能力有限,就不讲Paxos算法描述了,不过Paxos如此有名,甚至Paxos的原始论文都有翻译质量比较高的中文翻译了,还有精读注解。
Raft
为了解决复杂不堪,难以理解,工程上难以实现正确的Paxos算法,由此Raft算法诞生了。之前提到过,非Paxos算法的共识算法都是Paxos的阉割版。恩,Raft算法从理论上来讲确实不是一个完备的分布式共识算法。
为了可理解,可操作,可实践,Raft对一些复杂的处理做了一些简化,以保证绝大部分情况下都保证一致性。
Raft算法通过将分布式一致性的问题拆分为3个子问题来简化算法:Leader选举,日志复制,安全保证。Raft强化了leader的作用,通过leader来保证分布式一致性。Raft算法也详细说明了具体工程实现时的各种方案细节。比如,日志亚索,Client互交等。基本上对照算法论文就能够实现,而且代码量也不大。
Raft论文中提到只用了2000行C++代码就实现了算法。
ZAB
全称:Zookeeper Atomic Broadcast Protocol,是Zookeeper系统中采用的分布式共识算法。抛开各种细节,ZAB和Raft的实现类似,也是Paxos的阉割版。比如,强化leader的作用,通过leader来保证分布式一致性。
共识算法的比较
ZAB,Paxos和Raft有一个较大的差异就是复制的方式,Paxos和Raft采用的是状态机副本(state machine replication,又叫active replication),ZAB采用的是主备份(primary backup,又称passive replication)。
这样的思路有点像MySQL中的binlog,确实有点像。
注意:以上的共识算法的前提都是非拜占庭网络环境(一般是公司内网,不考虑存在恶意节点),如果是拜占庭网络环境(公网,一定要考虑恶意节点作恶),那么共识算法就不是这些了,拜占庭网络的共识算法一般是区块链相关的共识算法:PoW,PoS,DPoS,PBFT等。
EOF