分布式系统:
分布式存储:GFS,HDFS(HBase)
https://blog.csdn.net/weixin_45680007/category_9386481.html
分布式计算系统:MapReduce
https://blog.csdn.net/weixin_45680007/article/details/102628742
消息中间件:Kafka
分布式处理管理器:Zookeeper
https://blog.csdn.net/weixin_45680007/article/details/103603019
RocketMQ:
https://blog.csdn.net/weixin_45680007/article/details/104560236
负载均衡技术
https://blog.csdn.net/weixin_45680007/article/details/103654734
分布式缓存技术:Redis
https://editor.csdn.net/md/?articleId=104086505
分布式锁:
https://editor.csdn.net/md/?articleId=104086505
https://blog.csdn.net/weixin_45680007/article/details/103603019
分布式系统设计:
分布式优化策略:
https://blog.csdn.net/weixin_45680007/article/details/104679259
Java事务
用户操作的原子性 ( Atomicity )、一致性
( Consistency )、隔离性 ( Isolation ) 和持久性 ( Durabilily )
Connection:数据库连接
Statement:事物操作
try-catch:成功则提交,失败则rollback()
本地事物(即单个事物提交):仅依赖资源管理器
conn.setAutoCommit(false);
conn.commit();
conn.rollback();
分布式事务:依赖于事物管理器和资源管理器
1:userTx = (UserTransaction)getContext().lookup(“java:comp/UserTransaction”);初始化事物管理器
2:userTx.begin(); // 启动事务
3:userTx.commit();// 提交事务
4:userTx.rollback();// 事务回滚:数据库 A 和数据库 B 中的数据更新被同时撤销
先找最近记录,如果最近这一条记录事务id不符合条件,不可见的话,再去找上一个版本再比较当前事务的id和这个版本事务id看能不能访问,以此类推直到返回可见的版本或者结束
已提交读:每次查询的开始都会生成一个独立的ReadView(当前读)
可重复读:第一次读的时候生成一个ReadView,之后的读都复用之前的ReadView(快照读)
分布式存储系统
分布式算法/分布式事务
参考资料
CAP:一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)
C:数据是否具有强一致性
A:用户请求服务器能在一定时间内得到响应
P:当某个服务器宕机后,其他服务器能够继续提供服务
CA:单个服务器
假设现在有两个服务器AB
初始阶段两个服务器相同,当B更新数据后不巧宕机,此时有用户请求节点A,A有两种选择
CP:A不响应用户,而是更新数据,保持了一致性
AP:A响应用户,保持了可用性
BASE:
BA:基本可用
1:响应时间的基本可用 不需要立即响应,而是可以有略微的延时
2:功能的基本可用 分布式系统在出现故障时,系统允许损失部分可用性,即保证核心功能或者当前最重要功能可用。
S:软状态
软状态允许系统数据存在中间状态,但不会影响系统的整体可用性,即允许不同节点的副本之间存在暂时的不一致情况。
E:最终一致性
WAL(Write ahead logging)预写性日志
将数据的操作写入缓冲区和日志中,而不需要立即写入磁盘
redo log
redo log称为重做日志,每当有操作时,在数据变更之前将操作写入redo log,这样当发生掉电之类的情况时系统可以在重启后继续操作。
undo log
undo log称为撤销日志,当一些变更执行到一半无法完成时,可以根据撤销日志恢复到变更之间的状态。
lazywriter:刷新缓冲区读入磁盘
MySQL中用redo log来在系统Crash重启之类的情况时修复数据(事务的持久性),而undo log来保证事务的原子性
一致性保证:
强一致性:实时更新
顺序一致性:不保证实时,但是最终一致,也保证按顺序执行
弱一致性:仅仅保证最终一致性
强一致性算法:paxos,raft,zab
弱一致性:gossip,柔性事物
不保证一致性:2PC,3PC
两阶段提交(2-Phase Commit, 2PC)
事务的两阶段处理
1 准备阶段
事务协调者(事务管理器)给每个参与者(资源管理器)发送 Prepare 消息,每个参与者要么直接返回 失败(False),要么在本地执行事务,写本地的 redo 和 undo 日志,但不提交(Ready)
2 提交阶段:
如果协调者收到了参与者的失败消息或者超时,直接给每个参与者发送回滚(Rollback)消息;否则, 发送提交(Commit)消息;参与者根据协调者的指令执行提交或者回滚操作,释放所有事务处理过程中使用的锁资源。(committed)
第一阶段就是锁资源
缺点:
单点服务: 若协调者突然崩溃则事务流程无法继续进行或者造成状态不一致
无法保证一致性: 若协调者第二阶段发送提交请求时崩溃,可能部分参与者受到COMMIT请求提交了事务,而另一部分参与者未收到请求导致阻塞从而数据不一致
阻塞: 为了保证事务完成提交,各参与者在完成第一阶段事务执行后必须锁定相关资源直到正式提交,影响系统的吞吐量。
三阶段提交协议(3-Phase Commit, 3PC)
改进1:超时2:三个阶段
同样会写redo和undo两个日志,用于记录和回滚
CanCommit
协调者发送CanCommit请求
参与者协调者回复YES或NO,但不执行事务也不锁定资源
PreCommit
协调者发送PreCommit请求,执行事务预提交
各参与者执行事务,写事务日志但不进行提交。 各参与者锁定事务相关的资源
如果协调者收到失败,则abort。如果参与者此时在CanCommit阶段没有收到PreCommit超时,则也中断回滚事务
DoCommit
协调者DoCommit
参与者正式提交事务,并在完成后释放相关资源。
参与者协调者回复ACK,协调者收到所有参与者的ACK后认为事务提交成功。若有参与者回复NO或者超时,则回滚事务。
参与者进入 DoCommit 状态后,若始终未收到协调者的 DoCommit 请求则会超时后自动执行提交。(基于概率,因为认为进入第三阶段即使没有收到确认也会认为能够正确提交)
解决单点故障问题和阻塞,但不能解决数据一致性(在DoCommit回滚,但是部分参与者没收到)
单点故障:利用超时机制
第二阶段超时则自动回滚,第三阶段超时则自动提交
强一致性算法
核心思想:保证半数节点一致性,大多数
paxos:
参考资料
参考资料
每一个Acceptor都有
(MaxN,maxV)接受到的最大
(AcceptN,AcceptV)赞同的最大
准备阶段(占坑阶段)
第一阶段A:Proposer向所有的Acceptor广播Prepare(n)请求。
第一阶段B:Acceptor接收到Prepare(n)请求
若提议编号n比MaxnN都要大,则承诺将不会接收提议编号比n小的提议即讲MaxN=n,并且返回(AcceptN,AcceptV),否则不予理会。当AcceptN为null则直接返回null
接受阶段(提交阶段)
第二阶段A:Proposer得到了Acceptor响应
如果未超过半数accpetor响应,直接转为提议失败;
如果超过多数Acceptor的承诺,又分为不同情况:
如果所有Acceptor都未接收过值(都为null),那么向所有的Acceptor发起自己的值和提议编号n,记住,一定是所有Acceptor都没接受过值;
如果有部分Acceptor接收过value,那么从所有接受过的value中选择对应的N最大的作为提议的value,提议编号仍然为n。但此时Proposer就不能提议自己的value,只能信任Acceptor通过的value即不会改N,仍然是Prepare(n),只修改V
第二阶段B:Acceptor接收到提议后,如果该提议版本号不等于自身保存记录的版本号(第一阶段记录的),不接受该请求,相等则将KV都写入本地。
对于未能通知的角色:Proposer把消息传递给learner,由learner角色将确定的提议通知给所有accpetor
共识Raft
思想:基于复制状态机,即一组server从相同的初始状态起,按相同的顺序执行相同的命令,最终会达到一直的状态,一组server记录相同的操作日志,并以相同的顺序应用到状态机。日志复制
投票机制
server可以作为Leader、 Follower 、Candidate
每个节点上都有一个倒计时器 (Election Timeout),时间随机在 150ms 到 300ms 之间。有几种情况会重设 Timeout:
收到选举的请求
收到 Leader 的 Heartbeat
当计时器一旦超时,节点就会转化为Candidate,给其他server发送请求。先timeout的会先发送请求当然也会占得先机。
如果获得2/n+1票,则成为leader然后广播所有服务器停止投票阻止新得领导产生(Heartbeat让倒计时器重置)。
如果没有超过2/n+1票,则僵持等倒计时器超时后重新发起投票
每一轮只能投一次票
运行过程中,领导者周期性的向所有跟随者发送心跳包来维持自己的权威。如果一个跟随者在一段时间里没有接收到任何消息,也就是选举超时,那么他就会认为系统中没有可用的领导者,并且发起选举以选出新的领导者。
版本号term:每一轮新的领导者都会拥有选举轮次,如果之前的领导者恢复,因为选举轮次较小,自动降级为follower
数据一致性:
日志复制:leader会给所有follower发送日志,如果超过半数同意,则会提交数据(类似于两阶段提交)
1:client请求到达leader
leader首先将该请求转化成entry,然后添加到自己的log中,得到该entry的index信息。
然后发送给所有follower,follower收到后返回ACK
当某个entry已经被过半的follower复制了,则就将该entry提交,将commitIndex提升至该entry的index
2:然后在下一次heartBeat心跳中,将commitIndex就传给了所有的follower,对应的follower就可以将commitIndex以及之前的entry应用到各自的状态机中了
1:被选举出来的leader必须要包含所有已经比提交的entries
如leader针对复制过半的entry提交了,但是某些follower可能还没有这些entry,当leader挂了,该follower如果被选举成leader的时候,就可能会覆盖掉了上述的entry了,
ZAB:
( ZooKeeper Atomic Broadcast , ZooKeeper 原子消息广播协议)
ZXID:
64位ZXID =32位epoch+32位事务请求计数器每一个事务都有不同的ZXID
事务请求计数器:针对客户端每一个事务请求,计数器加 1;
epoch:当选产生一个新的Leader 服务器,就会从这个 Leader 服务器上取出其本地日志中最大事务的 ZXID,并从中读取 epoch 值,然后加 1,以此作为新的 epoch,并将低 32 位从 0 开始计数。作为身份标识,新的leader会有新的epoch,这样就算旧的 leader 崩溃恢复之后,也没有人听他的了,因为 follower 只听从当前年代的 leader 的命令(值最大的)
数据一致性:
1:客户端提交数据到leader
2:leader将数据复制给follower,observer
3:follower在本地写入后,返回ACK给leader
4:leader接收到半数以上的ACK后,本地提交并发送commit到follower
写入过程如果出现崩溃?
情况一:leader将数据复制给follower后崩溃?
情况二:leader在本地commit后,部分发送commit到follower后崩溃?
针对这些问题,ZAB 定义了 2 个原则:
同步已经在 Leader 提交的事务最终会被所有服务器提交。
丢弃只在 Leader 提出/复制,但没有提交的事务。
而如何满足上述原则呢?利用最大ZXID选取leader原则
情况一:没有follower有已经commit的最大ZXID,所以本事务丢弃
情况二:有follower有已经commit的最大ZXID,此follower会成为leader,并同步本事务
如果leader本地commit,其他follower没有一个commit(丢失)
投票机制:
恢复模式4阶段
选举阶段-选出准 Leader
1:Leader election(选举阶段):根据ZXID大小投票,节点得到超半数节点的票数,可以当选为准 leader。只有到达广播阶段(broadcast) 准 leader 才会成为真正的 leader因为此时还不满足数据一致性
发现阶段-接受提议、生成 epoch、接受 epoch
2: Discovery(发现阶段):followers 跟准 leader 进行通信,同步 followers 最近接收的事务提议ZXID,并接受新的epoch。
同步阶段-同步 follower 副本
3:Synchronization(同步阶段):利用 leader 前一阶段获得的最新提议历史, 同步集群中所有的副本。只有超过二分之一同步后,准 leader 才会成为真正的 leader才满足数据一致性。
广播阶段-leader 消息广播)
4:Broadcast(广播阶段):到了这个阶段,Zookeeper 集群才能正式对外提供事务服务
注意点:
1:持久节点(persistent)和临时节点(ephemeral)
持久节点只能通过delete删除。临时节点在创建该节点的客户端崩溃或关闭时,自动被删除。
而leader明显为临时节点
2:仲裁模式:法定人数必须大于一半,即满足多数原则,即可用服务器超过一半才代表系统可用
3:在某个electionEpoch轮次内,可以投多次票,只要遇到更大的票就更新,然后分发新的投票给所有人
选取leader的两种情况:
举例:
在集群正常工作之前,myid小的服务器给myid大的服务器投票
有 5 台服务器,编号分别是 1,2,3,4,5,按编号依次启动:
服务器 1 启动,给自己投票,然后发投票信息,由于其它机器还没有启动所以它收不到反馈信息,服务器 1 的状态一直属于 Looking。
服务器 2 启动,给自己投票,同时与之前启动的服务器 1 交换结果,由于服务器 2 的编号大所以服务器 2 胜出,但此时投票数没有大于半数(3票),所以两个服务器的状态依然是 LOOKING。
服务器 3 启动,给自己投票,同时与之前启动的服务器 1,2 交换信息,由于服务器 3 的编号最大所以服务器 3 胜出,此时投票数正好大于半数,所以服务器 3 成为领导者,服务器 1,2 成为小弟。
服务器 4 启动,给自己投票,同时与之前启动的服务器 1,2,3 交换信息,尽管服务器 4 的编号大,但之前服务器 3 已经胜出,所以服务器 4 只能成为小弟。
服务器 5 启动,后面的逻辑同服务器 4 成为小弟。
2:leader宕机
首先每一个server都会进入looking状态,并且会将自己设为推荐leader,数据结构为(推荐leader,zxid)
每一个server彼此交换投票意向,当收到所有server回复后,计算zxid最大的作为本server的推荐leader,更新数据结构(新leader,最大的zxid)
统计所有选票,超过半数的为胜利者
参考资料
ZAB和Raft的区别:
1:选举后是否有同步过程
2:
Raft中的每个server在某个term轮次内只能投一次票,这样就可能造成split vote,即各个candidate都没有收到过半的投票
ZooKeeper中的每个server,在某个electionEpoch轮次内,可以投多次票,只要遇到更大的票就更新,然后分发新的投票给所有人。这种情况下不存在split vote现象
3:传输过程中Leader挂掉,ZAB根据协议判断,RAFT是保持现状后面进行一致性检查
NWR:
参考资料
N:在分布式存储系统中,有多少份备份数据
W:代表一次成功的更新操作要求至少有w份数据写入成功
R: 代表一次成功的读数据操作要求至少有R份数据成功读取
强一致性:R+W>N,则读取操作和写入操作成功的数据一定会有交集,这样就可以保证一定能够读取到最新版本的更新数据
在满足数据一致性协议的前提下,R或者W设置的越大,则系统延迟越大,因为这取决于最慢的那份备份数据的响应时间
R+W<=N,则无法保证数据的强一致性,因为成功写和成功读集合可能不存在交集,这样读操作无法读取到最新的更新数值
柔性分布式事务
参考资料
可以把符合传统的 ACID 叫做刚性事务,把满足 BASE 理论的最终一致性事务叫做柔性事务。
柔性分布式事务的实现主要有以下 6 种方案:
XA 方案
TCC 方案
本地消息表
可靠消息最终一致性方案
最大努力通知方案
SAGA
XA:
目前 XA 有两种实现:
基于一阶段提交( 1PC ) 的弱 XA :遍历进行commit提交
基于二阶段提交( 2PC ) 的强 XA 。
幂等操纵:
在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。也就是说,同一个方法,使用同样的参数,调用多次产生的业务结果与调用一次产生的业务结果相同
TCC:Try-Confirm / Cancel:补偿性事务
Confirm / Cancel只能执行一种,对于A_B事务,如果某一个操作失败之行Cancel,另一个也需要执行cancel
Try:
尝试执行业务
完成所有业务检查(一致性)
预留必须业务资源(资源加锁)
Confirm:
确认执行业务;
真正执行业务,不作任何业务检查
只使用Try阶段预留的业务资源
Confirm 操作满足幂等性
Cancel:
取消执行业务
释放Try阶段预留的业务资源
Cancel操作满足幂等性
例子:A账户转账给B账户
本地消息表:分布式处理的任务通过消息日志的方式来异步执行
最终一致性方案:
1:A 系统先发送一个 prepared 消息到 mq,如果这个 prepared 消息发送失败那么就直接取消操作别执行了;
2:如果这个消息发送成功过了,那么接着执行本地事务,如果成功就告诉 mq 发送确认消息,如果失败就告诉 mq 回滚消息;
3:如果发送了确认消息,那么此时 B 系统会接收到确认消息,然后执行本地的事务;如果成功则mq消息完成,如果失败则不断重试。实在无法成功A也回滚
对于A,发送prepared但是没有发送确认消息:
mq 会自动定时轮询所有 prepared 消息回调A的接口,问A,这个消息是不是本地事务处理失败了,所有没发送确认的消息,是继续重试还是回滚?一般来说这里你就可以查下数据库看之前本地事务是否执行,如果回滚了,那么这里也回滚吧。这个就是避免可能本地事务执行成功了,而确认消息却发送失败了。
最大努力通知:
以支付通知为例,业务系统调用支付平台进行支付,支付平台进行支付,进行操作支付之后支付平台会尽量去通知业务系统支付操作是否成功,但是会有一个最大通知次数。如果超过这个次数后还是通知失败,就不再通知,业务系统自行调用支付平台提供一个查询接口,供业务系统进行查询支付操作是否成功。
Saga :
每个 Saga 由一系列 sub-transaction Ti 组成
每个Ti 都有对应的补偿动作 Ci ,补偿动作用于撤销 Ti 造成的结果。这里的每个 T ,都是一个本地事务。
可以看到,和 TCC 相比,Saga 没有“预留 try”动作 ,它的 Ti 就是直接提交到库。
Saga的执行顺序有两种:
子事务序列 T1, T2, …, Tn得以完成 (最佳情况)。
或者序列 T1, T2, …, Tj, Cj, …, C2, C1, 0 < j < n, 得以完成。
Gossip八卦算法:最终一致性算法
一个节点想要分享一些信息给网络中的其他的一些节点。于是,它周期性的随机选择一些节点进行传播
六度分隔
优势:
1:可扩展性
2:失败容错
缺点:
耗费资源