分布式事务seata

前言

在讲分布式事务之前,先来理一下事务,大家都知道事务有ACID四大特性。简单复习一下这四个特性。

  • 原子性(Atomicity):单个事务,为一个不可分割的最小工作单元。通过undolog来实现。
  • 一致性(Consistency):数据库总是从一个一致性的状态转换到另外一个一致性的状态。是最终目的。其它三个特性均是为了保证一致性。
  • 隔离性(Isolation):一个事务所做的修改在最终提交以前,对其他事务是不可见的。通过(读写锁+MVCC(通过readview和undolog版本链实现))来实现。
  • 持久性(Durability):一旦事务提交,则其所做的修改就会永久保存到数据库中。通过binlog、redolog来实现。

现在说清楚了事务的特性,但是大家都知道数据库支持的事务生效是对同一个数据库才生效的。但是现在的场景大部分都可能是分库分表的。而且从业务上来说也有可能一个业务同时操作多个数据库这个时候数据库提供的事务就没办法保证它所具有的特性了,所以在就有了能够支持分布式事务的框架,seata。

steata定义

seata官网还是非常好的,毕竟阿里开源,中文版,简单易懂!

seata官网

根据官网文档首先了解以下三个概念:

TC (Transaction Coordinator) - 事务协调者:维护全局和分支事务的状态,驱动全局事务提交或回滚。

TM (Transaction Manager) - 事务管理器:定义全局事务的范围:开始全局事务、提交或回滚全局事务。

RM (Resource Manager) - 资源管理器:管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

借用官网的图来描述它们的关系:

处理过程:

  1. TM 向 TC 申请开启一个全局事务,TC 创建全局事务后返回全局唯一的 XID,XID 会在全局事务的上下文中传播;
  2. RM 向 TC 注册分支事务,该分支事务归属于拥有相同 XID 的全局事务;
  3. TM 要求 TC 提交或回滚 XID 的相应全局事务;
  4. TC 在 XID 的相应全局事务下驱动所有分支事务以完成分支提交或回滚。

现在基本概念说完了,现在看下各个模式:

AT模式

AT详解参考链接

1、获取本地锁(select .... for update 实现)

2、通过1步骤的查询拿到修改前的镜像(beforeimage)。

3、执行sql语句。

4、再次查询拿到修改后的镜像(afterimage)。

5、向TC申请全局锁(XID)。

6、如果获取成功,就提交事务并把自己的操作信息提交上去。(sql语句、beforeimage、afteriamge等信息,是否成功等。)。如果获取失败。就自旋一直获取直到超时回滚。

7、所有数据库的操作都遵循1-6的步骤。

所以当整个业务完成后,TC就会根据各个RM上报的结果来决定是否回滚(通过sql语句、beforeimage、afteriamge等信息生成回滚语句)

如果需要提交,就异步和批量地删除相应 UNDO LOG 记录。

问题

为什么不先拿全局锁,而是先拿本地锁?

首先,能够肯定的是先获取全局锁肯定是不会有任何问题的。只是为了提升性能,就将本地锁先行获取了。

我们假设先获取全局锁。情况变成了串行调用,对于需要提升性能和响应的情况来说效率是非常低下的。

脏数据回滚失败如何处理?

  1. 脏数据需手动处理,根据日志提示修正数据或者将对应undo删除(可自定义实现FailureHandler做邮件通知或其他)
  2. 关闭回滚时undo镜像校验,不推荐该方案。

TCC模式

AT 模式基于 支持本地 ACID 事务 的 关系型数据库

  • 一阶段 prepare 行为:在本地事务中,一并提交业务数据更新和相应回滚日志记录。
  • 二阶段 commit 行为:马上成功结束,自动 异步批量清理回滚日志。
  • 二阶段 rollback 行为:通过回滚日志,自动 生成补偿操作,完成数据回滚。

相应的,TCC 模式,不依赖于底层数据资源的事务支持:

  • 一阶段 prepare 行为:调用 自定义 的 prepare 逻辑。
  • 二阶段 commit 行为:调用 自定义 的 commit 逻辑。
  • 二阶段 rollback 行为:调用 自定义 的 rollback 逻辑。

所谓 TCC 模式,是指支持把 自定义 的分支事务纳入到全局事务的管理中。

官网图如下:

从图中可以看出:一阶段准备完成后,如果commit异常了,就触发不了rollback了。

而且二阶段的rollback也是针对一阶段的prepare。所以一阶段如果成功的话,那么后面一定要成功。否则会一直死循环调用。

可能存在的问题:

1、空回滚。

在第一阶段时,由于网络原因导致调用超时,TM没有接收到RM第一阶段的返回,就认为调用失败了,就调用第二阶段的rollback,但是目前RM的第一阶段并未执行,空回滚了。

2、悬挂问题。

与上面情况相似。只是由于超时了已经执行了回滚操作。但是此时由于网络好了,又在执行try逻辑,但是此时已经执行过rollback(里面的某些需要的信息可能已经被清理)了。从而引发问题。

3、幂等问题。

二阶段commit或者rollback时由于网络超时,导致可能多次执行逻辑。所以需要幂等。

所以TCC考虑的场景会很多。编写代码就需要注意这些问题。

SAGA模式

Saga模式是SEATA提供的长事务解决方案,在Saga模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都由业务开发实现。

优势:

  • 一阶段提交本地事务,无锁,高性能
  • 事件驱动架构,参与者可异步执行,高吞吐
  • 补偿服务易于实现

缺点:

  • 不保证隔离性

Saga的实现:

类似于一个流程引擎。

基于状态机引擎的 Saga 实现:

目前SEATA提供的Saga模式是基于状态机引擎来实现的,机制是:

  1. 通过状态图来定义服务调用的流程并生成 json 状态语言定义文件
  2. 状态图中一个节点可以是调用一个服务,节点可以配置它的补偿节点
  3. 状态图 json 由状态机引擎驱动执行,当出现异常时状态引擎反向执行已成功节点对应的补偿节点将事务回滚

注意: 异常发生时是否进行补偿也可由用户自定义决定

4.可以实现服务编排需求,支持单项选择、并发、子流程、参数转换、参数映射、服务执行状态判断、异常捕获等功能

TCC的问题SAGA也存在:空回滚、悬挂、幂等。

缺乏隔离性的应对

  • 由于 Saga 事务不保证隔离性, 在极端情况下可能由于脏写无法完成回滚操作, 比如举一个极端的例子, 分布式事务内先给用户A充值, 然后给用户B扣减余额, 如果在给A用户充值成功, 在事务提交以前, A用户把余额消费掉了, 如果事务发生回滚, 这时则没有办法进行补偿了。这就是缺乏隔离性造成的典型的问题, 实践中一般的应对方法是:
    • 业务流程设计时遵循“宁可长款, 不可短款”的原则, 长款意思是客户少了钱机构多了钱, 以机构信誉可以给客户退款, 反之则是短款, 少的钱可能追不回来了。所以在业务流程设计上一定是先扣款。
    • 有些业务场景可以允许让业务最终成功, 在回滚不了的情况下可以继续重试完成后面的流程, 所以状态机引擎除了提供“回滚”能力还需要提供“向前”恢复上下文继续执行的能力, 让业务最终执行成功, 达到最终一致性的目的。

XA模式

设计和使用像AT,实现像TCC。暂时不是很理解,就贴官网链接吧

seata-XA模式

另外分享一个链接用于更加详细介绍各个模式:

分布式事务详解

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值