本地事务和分布式事务

时间:2022-12-07 18:59:51


1. 本地事务

1.1 @Transactional注解

@Transactional 是本地事务,在分布式系统中。只能控制住自己的回滚,控制不了其他服务的回滚。

1.1.1 isolation

1.1.1.1 READ UNCOMMITTED(读未提交)
该隔离级别的事务会读到其它未提交事务的数据, 此现象也称之为脏读。
1.1.1.2 READ COMMITTED( 读提交)
一个事务可以读取另一个已提交的事务, 多次读取会造成不一样的结果, 此现象称为不可重
复读问题, Oracle 和 SQL Server 的默认隔离级别。
1.1.1.3 REPEATABLE READ( 可重复读)
该隔离级别是 MySQL 默认的隔离级别, 在同一个事务里, select 的结果是事务开始时时间
点的状态, 因此, 同样的 select 操作读到的结果会是一致的, 但是, 会有幻读现象。 MySQL
的 InnoDB 引擎可以通过 next-key locks 机制( 参考下文"行锁的算法"一节) 来避免幻读。
1.1.1.4 SERIALIZABLE( 序列化)
在该隔离级别下事务都是串行顺序执行的, MySQL 数据库的 InnoDB 引擎会给读操作隐式
加一把读共享锁, 从而避免了脏读、 不可重读复读和幻读问题。

1.1.2 propagation

1.1.2.1. PROPAGATION_REQUIRED: 如果当前没有事务, 就创建一个新事务, 如果当前存在事务,就加入该事务, 该设置是最常用的设置。
1.1.2.2. PROPAGATION_SUPPORTS: 支持当前事务, 如果当前存在事务, 就加入该事务, 如果当前不存在事务, 就以非事务执行。
1.1.2.3. PROPAGATION_MANDATORY: 支持当前事务, 如果当前存在事务, 就加入该事务, 如果当前不存在事务, 就抛出异常。
1.1.2.4. PROPAGATION_REQUIRES_NEW: 创建新事务, 无论当前存不存在事务, 都创建新事务。
1.1.2.5. PROPAGATION_NOT_SUPPORTED: 以非事务方式执行操作, 如果当前存在事务, 就把当前事务挂起。
1.1.2.6. PROPAGATION_NEVER: 以非事务方式执行, 如果当前存在事务, 则抛出异常。
1.1.2.7. PROPAGATION_NESTED: 如果当前存在事务, 则在嵌套事务内执行。 如果当前没有事务,则执行与 PROPAGATION_REQUIRED 类似的操作。

1.1.3 timeout

1.2.SpringBoot 事务关键点

1.2.1. 事务的自动配置
TransactionAutoConfiguration
1.2.2. 事务的坑
在同一个类里面, 编写两个方法, 内部调用的时候, 会导致事务设置失效。 原因是没有用到
代理对象的缘故。
解决:
1.2.2.1 导入 spring-boot-starter-aop;引入了aspecj
1.2.2.2 @EnableTransactionManagement(proxyTargetClass = true)
1.2.2.3 @EnableAspectJAutoProxy(exposeProxy=true);开启aspectj动态代理,以后所有动态代理都是aspectj创建的(即使没有接口也可以创建动态代理);exposeProxy=true对外暴露代理对象
1.2.2.4 AopContext.currentProxy() 调用方法;用代理对象调用方法

2. 分布式事务

最大的原因:网络问题 + 分布式机器

eg: 分布式事务:订单服务下订单 -> 库存服务锁定库存 -> 用户服务扣减积分

问题1. 远程服务假失败

远程服务其实执行成功了,由于网络故障等没有返回。导致订单回滚,库存不滚。

问题2. 远程服务执行完成,下面的其他方法出现问题。导致订单回滚,库存不滚。

本地事务和分布式事务

2.1 CAP 定理与 BASE 理论

2.1.1. CAP 定理: AP
CAP 原则又称 CAP 定理, 指的是在一个分布式系统中
2.1.1.1 一致性(Consistency) 
在分布式系统中的所有数据备份, 在同一时刻是否同样的值。 (等同于所有节点访问同一份最新的数据副本)
2.1.1.2 可用性(Availability)
在集群中一部分节点故障后, 集群整体是否还能响应客户端的读写请求。 (对数据更新具备高可用性)
2.1.1.3 分区容错性(Partition tolerance)
大多数分布式系统都分布在多个子网络。 每个子网络就叫做一个区(partition) 。分区容错的意思是, 区间通信可能失败。 比如, 一台服务器放在中国, 另一台服务器放在美国, 这就是两个区, 它们之间可能无法通信。

CAP 原则指的是, 这三个要素最多只能同时实现两点, 不可能三者兼顾。一般来说, 分区容错无法避免, 因此可以认为 CAP 的 P 总是成立。 CAP 定理告诉我们,剩下的 C 和 A 无法同时做到。分布式系统中实现一致性的 raft 算法、 paxos.  http://thesecretlivesofdata.com/raft/

对于多数大型互联网应用的场景, 主机众多、 部署分散, 而且现在的集群规模越来越大, 所以节点故障、 网络故障是常态, 而且要保证服务可用性达到 99.99999%(N 个 9) , 即保证P 和A,舍弃 C。
 

本地事务和分布式事务


 

2.1.2. BASE 理论
是对 CAP 理论的延伸, 思想是即使无法做到强一致性(CAP 的一致性就是强一致性) , 但可
以采用适当的采取弱一致性, 即最终一致性

BASE 是指:
2.1.2.1 基本可用(Basically Available)
 基本可用是指分布式系统在出现故障的时候, 允许损失部分可用性(例如响应时间、功能上的可用性) , 允许损失部分可用性。 需要注意的是, 基本可用绝不等价于系统不可用。
 响应时间上的损失: 正常情况下搜索引擎需要在 0.5 秒之内返回给用户相应的查询结果, 但由于出现故障(比如系统部分机房发生断电或断网故障) , 查询结果的响应时间增加到了 1~2 秒。
 功能上的损失: 购物网站在购物高峰(如双十一) 时, 为了保护系统的稳定性,部分消费者可能会被引导到一个降级页面
2.1.2.2 软状态( Soft State)
软状态是指允许系统存在中间状态, 而该中间状态不会影响系统整体可用性。 分布式存储中一般一份数据会有多个副本, 允许不同副本同步的延时就是软状态的体现。 mysql replication 的异步复制也是一种体现。
2.1.2.3 最终一致性( Eventual Consistency)
 最终一致性是指系统中的所有数据副本经过一定时间后, 最终能够达到一致的状态。 弱一致性和强一致性相反, 最终一致性是弱一致性的一种特殊情况。

2.1.3. 强一致性、 弱一致性、 最终一致性
从客户端角度, 多进程并发访问时, 更新过的数据在不同进程如何获取的不同策略, 决定了不同的一致性。 对于关系型数据库, 要求更新过的数据能被后续的访问都能看到, 这是强一致性。 如果能容忍后续的部分或者全部访问不到, 则是弱一致性。 如果经过一段时间后要求能访问到更新后的数据, 则是最终一致性。

2.2 分布式事务几种方案

2pc: 询问 -> 提交 cancommit -> docommit

3pc: 询问 -> 准备 -> 提交 cancommit -> precommit -> docommit

TCC:准备 -> 提交 -> 回滚 try -> confirm -> cancel

刚性事务: 遵循 ACID 原则, 强一致性。2pc;3pc
柔性事务: 遵循 BASE 理论, 最终一致性;与刚性事务不同, 柔性事务允许一定时间内, 不同节点的数据不一致, 但要求最终一致。TCC;柔性事务-最大努力通知型方案;柔性事务-可靠消息+最终一致性方案( 异步确保型)

一. 使用框架:2pc, 3pc

2.2.1 刚性事务-2PC 模式

数据库支持的 2PC【 2 phase commit 二阶提交】 , 又叫做 XA Transactions

XA 性能不理想,在 mysql 数据库中支持的不太理想。

第一阶段: 事务协调器要求每个涉及到事务的数据库预提交(precommit)此操作, 并反映是否可以提交.
第二阶段: 事务协调器要求每个数据库提交数据。
其中, 如果有任何一个数据库否决此次提交, 那么所有数据库都会被要求回滚它们在此事务
中的那部分信息。

实现:spring-cloud-alibaba-seata  默认AT模式

二. TCC

2.2.2 柔性事务-TCC 事务补偿型方案

本地事务和分布式事务

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

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

三. 利用消息队列的通知型方案。保证最终一致性,支持大并发(干完事不用等,发送给消息队列慢慢同步)

2.2.3 柔性事务-最大努力通知型方案

按规律进行通知, 不保证数据一定能通知成功, 但会提供可查询操作接口进行核对。 这种方案主要用在与第三方系统通讯时, 比如: 调用微信或支付宝支付后的支付结果通知。 这种方案也是结合 MQ 进行实现, 例如: 通过 MQ 发送 http 请求, 设置最大通知次数。 达到通知次数后即不再通知。

2.2.4 柔性事务-可靠消息+最终一致性方案( 异步确保型)(解决异构,不同语言的代码兼容)

实现: 业务处理服务在业务事务提交之前, 向实时消息服务请求发送消息, 实时消息服务只记录消息数据, 而不是真正的发送。 业务处理服务在业务事务提交之后, 向实时消息服务确认发送。 只有在得到确认发送指令后, 实时消息服务才会真正发送。

防止消息丢失:
1、 做好消息确认机制( pulisher, consumer【 手动 ack】 )
2、 每一个发送的消息都在数据库做好记录。 定期将失败的消息再次发送一
 

实现:定时任务扫描;RabbitMQ 延时队列,基于消息TTL和死信Exchange结合