saga(一种消息驱动的本地事务序列)
2PC
- 参考:https://zhuanlan.zhihu.com/p/35616810
- 协调者询问参与者是否可以正常执行
- 参与者执行事务但不提交
- 协调者根据结果发起通知(提交或回滚)
- 协调者询问参与者是否可以正常执行
- 缺点
- 同步阻塞
- 参与事务这个阶段,资源被锁住
- 单点故障
- 协调者故障,参与者阻塞
- 特别是第二阶段的时候,大家都等着你发送消息呢
- 协调者故障,参与者阻塞
- 数据不一致
- 第二步的通知如果参与者没有收到,那么就会数据不一致
- 无法解决的问题
- 二阶段,协调者发出commit之后宕机,唯一接受到commit的机器宕机。那么就没人知道这个事务到底执行怎么样了。
- 同步阻塞
3PC
- 步骤
- can commit
- 协调者询问是否可以执行
- pre commit
- 协调者发送预执行
- do commit
- 协调者发送commit或中断
- can commit
- 与2PC的区别
- 如果3阶段,参与者没有收到消息,超时之后,提交
- can commit,不锁资源,减少可能的资源浪费
SAGA
- 事务的执行分布在几个服务中
- 串行执行
- 三部分
- 可补偿性事务
- 它后面的事务失败,它需要回滚,但是它已经提交了,所以在后续任务失败时,它需要补偿。
- 补偿方式之一:比如说订单创建,设置一个状态,表示订单状态。创建过程中,插入了一条新订单,其状态为creatting,但后续验证失败了,那么补偿性动作就是,将状态置为reject
- 这种状态叫做语义锁。表示已经提交,但可能发生更改
- 当然,事务的最后是需要把状态变成approve的。
- 补偿方式之一:比如说订单创建,设置一个状态,表示订单状态。创建过程中,插入了一条新订单,其状态为creatting,但后续验证失败了,那么补偿性动作就是,将状态置为reject
- 它后面的事务失败,它需要回滚,但是它已经提交了,所以在后续任务失败时,它需要补偿。
- 关键性事务
- 后面跟着不可能失败的事务
- 这个成功之后,后面的事务必定成功
- 后面跟着不可能失败的事务
- 可重复性事务
- 总是会成功。如果不成功,就重试,直到成功
- 比如说把订单状态变成approve。只是修改一个字段的值嘛
- 总是会成功。如果不成功,就重试,直到成功
- 可补偿性事务
- 三部分
协同式saga
- saga的逻辑和执行顺序写入到参与的服务中
- 假如执行顺序是A ->B -> C
- A执行成功之后,发布消息
- B接收到A的消息,执行
- C接受到B的消息,执行
- 假如执行顺序是A ->B -> C
- 好处
- 简单
- 问题
- 可能存在循环依赖
- 比较难以理解
- 很难看到这个事务到底涉及哪几个服务
- 紧耦合风险
- B执行的这个事务,可能受到很多事件的影响,那么它要关注多个A的消息事件
编排式saga
- 有一个编排器类,告诉参与方该做什么事情
- 编排器建模好方法
- 状态机
- 一组状态和一组由事件触发的状态转化组成
- 状态机
- 好处
- 简单依赖关系
- 不会引入循环依赖
- 编排器依赖参与方
- 参与方不依赖编排器
- 较少的耦合
- 参与方只提供服务API,不关心发布的事件
- 就是我提供服务,你要用就来调我。我不关心你的情况(发布的事件)
- 参与方只提供服务API,不关心发布的事件
- 关注点隔离,简化业务逻辑
- 参与者只需关系自己的业务
- 编排器只需关注状态转换
- 简单依赖关系
- 弊端
- 编排器可能存在过多的业务逻辑
- 问题
- saga执行过程中,如果读取/修改数据,可能暴露数据不一致,怎么办?
- 是事务隔离问题
- 解决方法
- 语义锁
- 比如订单的创建状态
- 合理安排执行顺序
- 重读值
- 作用:防止丢失更新
- 做法:在更新之前,读取数据,验证是否做过修改
- 读主库
- 语义锁
- saga执行过程中,如果读取/修改数据,可能暴露数据不一致,怎么办?