分布式理论
CAP定理
一致性(C):
在分布式系统中的所有数据备份,「在同一时刻是否拥有同样的值」。(等同于所有节点访问同一份最新的数据副本)
可用性(A):
在集群中一部分节点「故障」后,集群整体「是否还能响应」客户端的读写请求。(对数据更新具备高可用性)
分区容错性(P):
即使出现「单个组件无法可用,操作依然可以完成」。
分布式事务解决方案
《一》两阶段提交(2PC)
熟悉mysql的同学对两阶段提交应该颇为熟悉,mysql的事务就是通过「日志系统」来完成两阶段提交的。
两阶段协议可以用于单机集中式系统,由事务管理器协调多个资源管理器;也可以用于分布式系统,「由一个全局的事务管理器协调各个子系统的局部事务管理器完成两阶段提交」。
这个协议有「两个角色」,
A节点是事务的协调者,B和C是事务的参与者。
事务的提交分成两个阶段
第一个阶段是「投票阶段」
- 1.协调者首先将命令「写入日志」
- 2. 「发一个prepare命令」给B和C节点这两个参与者
- 3.B和C收到消息后,根据自己的实际情况,「判断自己的实际情况是否可以提交」
- 4.将处理结果「记录到日志」系统
- 5.将结果「返回」给协调者
第二个阶段是「决定阶段」
当A节点收到B和C参与者所有的确认消息后
- 「判断」所有协调者「是否都可以提交」
- 如果可以则「写入日志」并且发起commit命令
- 有一个不可以则「写入日志」并且发起abort命令
- 参与者收到协调者发起的命令,「执行命令」
- 将执行命令及结果「写入日志」
- 「返回结果」给协调者
存在问题:
-
「单点故障」:一旦事务管理器出现故障,整个系统不可用
-
「数据不一致」:在阶段二,如果事务管理器只发送了部分 commit 消息,此时网络发生异常,那么只有部分参与者接收到 commit 消息,也就是说只有部分参与者提交了事务,使得系统数据不一致。
-
「响应时间较长」:整个消息链路是串行的,要等待响应结果,不适合高并发的场景
-
「不确定性」:当事务管理器发送 commit 之后,并且此时只有一个参与者收到了 commit,那么当该参与者与事务管理器同时宕机之后,重新选举的事务管理器无法确定该条消息是否提交成功。
《二》三阶段提交(3PC)
三阶段提交又称3PC,相对于2PC来说增加了CanCommit阶段和超时机制。如果一段时间内没有收到协调者的commit请求,那么就会自动进行commit,解决了2PC单点故障的问题。
-
第一阶段:「CanCommit阶段」这个阶段所做的事很简单,就是协调者询问事务参与者,你是否有能力完成此次事务。
- 如果都返回yes,则进入第二阶段
- 有一个返回no或等待响应超时,则中断事务,并向所有参与者发送abort请求
- 第二阶段:「PreCommit阶段」此时协调者会向所有的参与者发送PreCommit请求,参与者收到后开始执行事务操作,并将Undo和Redo信息记录到事务日志中。参与者执行完事务操作后(此时属于未提交事务的状态),就会向协调者反馈“Ack”表示我已经准备好提交了,并等待协调者的下一步指令
- 第三阶段:「DoCommit阶段」在阶段二中如果所有的参与者节点都可以进行PreCommit提交,那么协调者就会从“预提交状态”转变为“提交状态”。然后向所有的参与者节点发送"doCommit"请求,参与者节点在收到提交请求后就会各自执行事务提交操作,并向协调者节点反馈“Ack”消息,协调者收到所有参与者的Ack消息后完成事务。相反,如果有一个参与者节点未完成PreCommit的反馈或者反馈超时,那么协调者都会向所有的参与者节点发送abort请求,从而中断事务。
《三》补偿事务(TCC)【「Try,Confirm,Cancel」】
TCC其实就是采用的补偿机制,其核心思想是:「针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作」
- Try阶段主要是对「业务系统做检测及资源预留」,其主要分为两个阶段
- Confirm 阶段主要是对「业务系统做确认提交」,Try阶段执行成功并开始执行 Confirm阶段时,默认 Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。
- Cancel 阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,「预留资源释放」。
《四》本地消息表
执行流程:
-
消息生产方,需要额外建一个消息表,并「记录消息发送状态」。消息表和业务数据要在一个事务里提交,也就是说他们要在一个数据库里面。然后消息会经过MQ发送到消息的消费方。
- 如果消息发送失败,会进行重试发送。
-
消息消费方,需要「处理」这个「消息」,并完成自己的业务逻辑。
- 如果是「业务上面的失败」,可以给生产方「发送一个业务补偿消息」,通知生产方进行回滚等操作。
- 此时如果本地事务处理成功,表明已经处理成功了
- 如果处理失败,那么就会重试执行。
-
生产方和消费方定时扫描本地消息表,把还没处理完成的消息或者失败的消息再发送一遍。
《五》消息事务
消息事务的原理是将两个事务「通过消息中间件进行异步解耦」,和上述的本地消息表有点类似,但是是通过消息中间件的机制去做的,其本质就是‘将本地消息表封装到了消息中间件中‘。
执行流程:
- 发送prepare消息到消息中间件
- 发送成功后,执行本地事务
- 如果事务执行成功,则commit,消息中间件将消息下发至消费端
- 如果事务执行失败,则回滚,消息中间件将这条prepare消息删除
- 消费端接收到消息进行消费,如果消费失败,则不断重试
《六》最大努力通知
执行流程:
- 系统 A 本地事务执行完之后,发送个消息到 MQ;
- 这里会有个专门消费 MQ 的服务,这个服务会消费 MQ 并调用系统 B 的接口;
- 要是系统 B 执行成功就 ok 了;要是系统 B 执行失败了,那么最大努力通知服务就定时尝试重新调用系统 B, 反复 N 次,最后还是不行就放弃。
《七》Sagas 事务模型
其核心思想是「将长事务拆分为多个本地短事务」,由Saga事务协调器协调,如果正常结束那就正常完成,如果「某个步骤失败,则根据相反顺序一次调用补偿操作」。