分布式总结
CAP理论
假设存在多个数据库服务,主数据库和多个从数据库.,如下图:
分布式环境
C(Consistency)一致性:如果主数据库完成数据更新,则从数据库也必须是更新后的值,从任何一个数据库中读取的都是最新的数据,可以超时但是不可以返回旧数据。
实现方式:主数据库向所有从数据库同步数据,同时所有参与同步的从数据库必须是锁表并且暂停服务以满足数据更新之后,调用者可以查询到最新的数据。
A(Availability) 可用性:即调用者不论什么时候都能访问到数据库服务,数据库服务不会响应超时或者错误。
实现方式:只要数据库不宕机,就一直对外提供服务,即使是数据更新期间也要提供服务。这就注定与一致性相冲突。
P(Partition Tolerance)分区容错性:当部分服务发生故障或者网络发生延迟超时,不影响核心业务对外提供服务。
CAP是分布式下的三个特征,但是现在的分布式环境下无法完全满足这三个特性,三者只能取其二。
- 如果保证CA,则需要系统在更新数据之后,就可以从从数据库中读取新数据,也即主从数据同步过程与数据更新过程同时完成。这在分布式环境几乎是不可能的,除非是单机系统。
- 如果保证CP,则需要牺牲可用性,保证数据的强一致性,这在银行,金融等领域比较常见,例如转账服务需要确保一方扣款后,另一方一定立刻收到款。
- 如果保证AP,则是牺牲一致性,保证数据的可用性,这是现在采取的比较多的方案。必须保证服务随时可用,但是用户更新数据后,可以等一段时间再查看到同步后的最新数据。
BASE理论
BASE理论是CAP理论的一个折中方案。相当于AP的一种变体。
BASE是指Basically Available(基本可用)、Soft-state( 软状态)、Eventual Consistency(最终一致性)
①Basically Available(基本可用):指分布式系统在出现故障的时候,允许损失部分可用性,保证核心可用。
②Soft-state( 软状态):主数据发生变更后,多个从数据库可能不会都发生变更,而是其中一些发生了变更,另外一些没有发生变更。例如下单过程中支付中等等。
③Eventual Consistency(最终一致性):数据变更一段时间后所有的数据库服务都会是最新的数据。
一致性分类:
- 强一致性:主数据库发生数据变更后,从数据库立刻变更。从从数据库中也能马上查询到最新的数据。
- 弱一致性:主数据库发生变更,但是从数据可能不会发生数据变更,即使是一段时间之后。
- 最终一致性:主数据发生变更后,从数据不会立刻发生变更,但一段时间后,可以保证从数据一定发生变更。
一致性hash
以前在分布式缓存中,为了让缓存信息均匀打到每个服务上,我们通常采用hash(name)%N算法来完成。但是这种方式存在一些问题。比如如果某台缓存服务上下线,则根据hash(name)%N算法,之前所有缓存服务器上的缓存信息都会需要重新定位,这样就会发生缓存雪崩,为了防止这种情况发生,人们发明了一致性hash算法。如图:
一致性hash环
①首先构造一个2^32(0 - 2^32-1)的hash环,然后先将缓存服务器(例如A,B,C)根据hash算法分配到hash环上。
②然后将缓存信息(例如1,2,3,4,5)也根据hash算法分配到hash环上。这样就算完成了。
- 但是如果我要查询某个缓存信息需要到哪台服务器上取数据?,例如查询缓存数据5
note:这时我们的缓存查找的方式就和原来不一样了。我们现在先根据缓存信息计算它在hash环上的位置,然后顺时针查找距离它最近的缓存服务器,这样就找到了A服务器。 -
如果新增了一个缓存服务器D,如图,那么会发生什么?
新增服务器
note:这时候会在hash环上插入一条D,如图。这个时候如果有缓存信息分布在DA之间(例如6),那么缓存信息就不会发生失效;但是如果缓存信息分布在CD之间(例如5),那么这个区间的缓存信息就会失效,但是不会影响DA,AB,BC之间(例如1,2,3,4)的缓存信息。这样可以大幅减少缓存失效的面积。
-
缓存倾斜:如果所有的缓存数据都分布在CA这段区间上,那么再新增一个节点,也很有可能发生缓存大面积失效的情况,如左图,这种问题该怎么解决?
缓存倾斜
note:这种情况可以通过设置虚拟节点的方式,具体流程是,将ABC服务器节点克隆多份,然后根据hash算法放在hash环上,如右图,A1与A11都是A的克隆节点。完成这步后同样的方式将缓存信息也放在环上,然后放在此时放在A11,B11区间的数据按照AB区间的逻辑处理。例如数据6的下一步缓存服务器是B11,也将放在B服务器上。
一致性算法
Gossip协议
当集群中有机器上线或者下线,需要通知集群中其他机器,怎样才能让所有的机器都知道这个消息,这就需要使用Gossip协议,Gossip协议是指通过周期性的像集群中的其他节点发送消息,直到所有的节点都得到这个消息。
Gossip协议的特点:
(1)Gossip协议是周期性散播消息,每隔一段时间传播一次
(2)被感染的节点,每次可以继续散播N个节点
(3)每次散播消息时,都会选择尚未发送过的节点进行散播,不会向发送的节点散播
(4)同一个节点可能会收到重复的消息,因为可能同时多个节点正好向它散播
(5)集群是去中心化的,节点之间都是平等的
(6)消息的散播不用等接收节点的 ack,即消息可能会丢失,但是最终应该会被感染
①假定种子选手是A,A会周期的向临近节点BC发消息
②BC节点会向它们的临近节点DE,D发消息,
③此时D会收到两次消息,最后D向F发消息
这样在经过几个周期性的传输后,所有的节点都会得到相同的消息通知。
Raft一致性协议
为了解决分布式环境下数据不一致性的情况,人们设计了Raft算法,这是一种在数据一致性和性能上的折中方案。
首先Raft将节点分为三种角色:
Leader:接受客户端请求,并将数据变更写成log日志并同步到follower
Follower:接收Leader同步过来的数据
Candidate:用于选举成为Leader
- 选举流程
①首先每个节点都是Candidate角色,都有term值且都为0,而且都会有一个随机睡眠时间。此后A节点最先苏醒,然后开始投自己一票,term++,然后向所有的节点发送选举请求。
②其他节点收到A节点的选举请求,首先会比较A的term和自己的term,如果大于或者等于自己的term,就变成follower角色然后回复A节点同意;如果term小于自己的term,就拒绝请求,并继续保持Candidate角色。
③当A节点收到过半的应答请求后就成为Leader节点,如果没有节点获得过半的票数,则继续随机睡眠一段时间后继续选举。
④A节点当选后,会定期向follower发送心跳消息,其他节点收到心跳后。其他节点就会清空定时器。
⑤如果此时A节点过了,那么各个follower节点将收不到心跳消息,然后定时器会重新开始,触发新一轮的选举。
- 数据日志复制流程
客户端的写请求只能访问Leader,流程如图:
①Leader会将数据写入到log日志中,此时还是Uncommited状态;
②向所有的follower发送日志同步请求;
③follower节点会将请求保存到本地,然后回复OK,此时所有的follower节点也还是Uncommited状态;
⑤Leader收到到过半OK后会认为该写请求生效,然后回复client写请求成功,并将状态改成Commited;
⑥Leader向所有follower节点发送写请求,将状态改成Commited。