分布式事务
为什么要有分布式事务?
- 跨服务协同时候,例如订单从提交到检测到调用财务系统到更新用户系统,这些操作需要保证原子性,没有事务串起来的话中间崩了会导致操作丢失
- 数据库过大,做了分库分表,要统一对多个数据库进行操作
分布式事务解决方案
- 2PC
prepare() commit() 准备和提交阶段,
操作时候,服务端向本次事务的参与端发送一个prepare()命令,参与者准备完毕返回ack,所有参与者准备完毕了才可以进行下一阶段操作
第二阶段是commit / abort操作,分为回滚和提交,协调者检查第一阶段参与者提交的prepare消息,如果第一阶段有任何一个参与者ack返回的是失败状态,返回参与者abort状态,否则执行commit返回结果
2PC作为强一致性协议,存在单点阻塞问题,因为协调者是单点的,如果协调者出现故障,也有几个情况
- 如果所有参与者事物都提交了prepare,且此时事务没有提交,那么可以选举新的协调者提交
- 如果由于网络原因部分参与者提交,部分没有成功提交,此时换上新的协调者并不能得到所有参与者的prepare消息,但是网络原因参与者的prepare ack已经丢失,导致阻塞
- 事务提交了,此时协调者坏了,那么第一种是第二阶段执行的是回滚事务操作,那么答案是不断重试,直到所有参与者都回滚了,不然那些在第一阶段准备成功的参与者会一直阻塞着。第二种是第二阶段执行的是提交事务操作,那么答案也是不断重试,因为有可能一些参与者的事务已经提交成功了,这个时候只有一条路,就是头铁往前冲,不断的重试,直到提交成功,到最后真的不行只能人工介入处理。
- 3PC
3PC解决了2PC一些问题
在2PC上变成了cancommit, precommit, commit三个阶段,理论上不会再阻塞
事务开始时,协调者向所有参与者发送cancommit ,所有参与者返回的ack都是yes则进入precommit,否则abort
在第二个阶段时候,所有参与者执行事务,写redolog,拿锁,把结果返回协调者,但是整个事务没有真正提交
第三个阶段,所有的参与者都操作完了之后,commit事务,执行发布或者回滚
不会阻塞原因主要在第二阶段,precommit设置了超时机制,如果协调者宕机了,事务执行完了之后过了超时时间自己调用commit执行提交或者回滚操作
- TCC
try-confirm-cancel机制
第一阶段,所有参与者主动调用try接口,执行模拟事务操作,但不会真正提交,先用现有的资源模拟一下是否可以执行事务,
不可以abort
否则进入第二阶段
confirm,所有参与者调用confirm,真正执行事务逻辑并写redolog,拿锁,但是不提交
当所有的事务都confirm成yes,commit事务,否则cancel
- Sago
最终补偿机制
在微服务架构中:1
2
3
4用户下单
├─ 创建订单(订单服务)
├─ 扣库存(库存服务)
└─ 扣余额(账户服务)
如果流程是:
1 创建订单成功
2 扣库存成功
3 扣余额失败
这时候系统状态变成:
订单存在
库存减少
余额未扣
数据已经 不一致。如果是单机事务可以ROLLBACK,但跨服务无法直接回滚
于是就需要:补偿操作
Sago是对于最终补偿机制的一个实现,
事务执行操作如果是1
A -> B -> C
那么相对应有回滚操作1
C' -> B' -> A'
补偿失败可以设置重试队列 / 人工介入
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 梦始!
评论
