27.分布式事务

上一章的订单方法使用的本地事务的问题:

1. 库存服务锁定成功了,但是网络原因返回数据途中有问题怎么办---》远程服务假失败:远程服务其实成功了,由于网络故障等没有返回导致订单回滚但是库存却扣减了

2.库存服务锁定成功了,库存服务下面的逻辑发生故障,然后订单回滚了,库存是远程调用无法回滚,怎么处理--》远程服务执行完成,下面的其他方法抛出异常导致订单回滚但是库存却扣减了

本地事务:分布式系统,只能控制住自己的回滚,控制不了其他服务的回滚

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

本地事务回顾

数据库事务的基本特性:

  • 原子性质:一系列操作整体不可拆分要么同时成功要么同时失败
  • 一致性:数据在事务的前后业务整体一致 A有100,B有20,A给B转10,那么A就是90,B就是30
  •  隔离性:事务之间隔离
  • 持久性:事务一旦成功,数据一定会落盘在数据库

事务的传播行为:

如果方法a,b,c都是一个事务,其中a调用了b和c,如果b的事务传播行为是propagation=Propogation.REQUIRED 那么a和b就共用一个事务,如果c的事务传播行为是propagation=Propogation.REQUIRES_NEW那么a和c就不共用一个事务,现在在a的方法最底下出现了一个异常,b会跟着a一起回滚,但是c就不会回滚,而且b会继承a的设置,自己的设置会无效

ps:直接调用同一个类里的事务方法会导致事务失效(因为aop的原因)

声明式事务基于Spring AOP实现,将具体业务逻辑与事务处理解耦,在 Spring 的 AOP 代理下,只有目标方法由外部调用,目标方法才由 Spring 生成的代理对象来管理,这会造成自调用问,spring的@Transactional事务生效的一个前提是进行方法调用前经过拦截器TransactionInterceptor,也就是说只有通过TransactionInterceptor拦截器的方法才会被加入到spring事务管理中,如果是在同一个类中的方法调用,则不会被方法拦截器拦截到,因此事务不会起作用,必须将方法放入另一个类,并且该类通过spring注入。

解决:

  1. 引入aop模块依赖spring-boot-starter-aop,使用aspectJ动态代理,开启主类注解@EnableAspectJAutoProxy(exposeProxy=true)(即使没有接口也可以创建动态代理)
  2. 使用代理对象的本类互相调用,AoPContext.currentProxy();转为当前类类型然后使用这个对象调用类里的b,c事务方法

事务的隔离级别:

  • READUNCOMMITTED:未提交的都可以读到,导致脏读问题
  • READCOMMITTED:读已提交,sql server和oracle的默认隔离级别
  • REPEATABLEREAD:可重复读,MySql默认隔离级别,在同一个事务里,select的结果是事务开始时间点的状态,因此,相同的select操作读到的结果会是一样的,但是会有幻读现象(InnoDB引擎可以通过next-key locks机制来避免幻读)幻读(指的是一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的数据行)
  • SERIALIZABLE:序列化,在该隔离级别下事务都是串行顺序执行的,从而避免了脏读,不可重复读和幻读问题

分布式事务

CAP定理:三个要素最多实现两点,不可能三者兼顾

  1. 一致性(C):在分布式系统中的所有数据备份,在同一时刻是否是同样的值(等同于所有节点访问同一一份最新的数据副本)
  2. 可用性(A):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求(对数据更新具备高可用性)
  3. 分区容错性(P):大多数分布式系统都分布在多个子网络,每个子网络叫做一个区。分区容错的意思是,区间通信可能失败,比如一台服务器放在中国,另一台放在美国,这就是两个区,他们直接可能无法通行

 分区容错是一定要满足的,我们只能子啊可用性和一致性中二选一

实现一致性的Raft算法:

一个节点有以下三个状态

  • Leader:领导者,一个集群里只能存在一个Leader。
  • Follower:跟随者,follower是被动的,一个客户端的修改数据请求如果发送到Follower上面时,会首先由Follower重定向到Leader上,
  • Candidate:参与者,一个节点切换到这个状态时,将开始进行一次新的选举。

一开始都是以随从状态启动,如果一个随从没有听到领导的命令,那么他就会变成一个候选者,候选者会发起投票,只要大多数节点投票通过,他就会成为一个领导者,这个过程叫领导选举过程。领导选举过程中有两个超时时间,第一个叫选举超时时间(也叫节点自旋时间),150ms-300ms,指的是一个节点在没有听到领导之后等到成为候选者的时间。第二个叫心跳时间,在心跳的时候会同步日志信息,领导和跟随者维持联系的时间,少于节点自旋时间,这个心跳会一直维持直到另一个节点停止接收心跳,成为候选者。

如果出现了两个候选者,两个领导者会重新开始自旋,直到选举只有一个领导。

客户端给领导发送一个数据,领导在日志里保存这个数据,但是没有提交,别的节点拿不到数据,接着领导提交日志数据,随从接受到数据后设置数据进日志里后会响应领导,领导等待大多数节点的响应完成之后,领导正式提交数据,领导提交成功后告诉其他节点现在可以提交了,其他节点提交节点,到这领导才会给客户端响应保存成功,这个过程叫日志复制,如果在数据传输过程中,由于网络中断,产生了两个领导A,B,而后网络恢复,产生最后的领导A,那么有B领导的子节点的没有提交的数据会全部回滚

BASE理论

是对CAP理论的延伸,思想是即使无法保证强一致性质(CAP的C表示的是强一致),但是可以采用适当的方法实现弱一致性

  • 基本可用(Basically Available): 基本可用是指分布式系统在出现故障的时候,允许损失部分可用性,即保证核心可用。电商大促时,为了应对访问量激增,部分用户可能会被引导到降级页面,服务层也可能只提供降级服务。这就是损失部分可用性的体现。
  • 软状态(Soft State): 软状态是指允许系统存在中间状态,而该中间状态不会影响系统整体可用性。分布式存储中一般一份数据至少会有三个副本,允许不同节点间副本同步的延时就是软状态的体现。MySQL Replication 的异步复制也是一种体现。
  • 最终一致性(Eventual Consistency): 最终一致性是指系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。弱一致性和强一致性相反,最终一致性是弱一致性的一种特殊情况。

 强一致要求更新的数据马上就能看到,弱一致能容忍后续的部分或者全部访问不到,经过一段时间后要求访问到更新后的数据就是最终一致性

分布式事务的几种方案

2PC模式:也叫XA协议,刚性事务遵循ACID原则,性能不理想不能放在高并发的情况下使用

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

TCC事务补偿型方案:柔性事务,遵循BASE理论

把自定义的分支事务纳入到全局事务的管理中,三个阶段行为分别是:prepare行为,commit行为,rollback行为

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

按规律进行通知,不保证数据一定能通知成功,但是会提供可查询操作接口进行核对,这种方案常用于与第三方系统通讯时。

可靠消息+最终一致性方案-柔性事务

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

seata环境准备

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

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

TM:事务管理器,定义全局事务的范围,开始全局事务,提交或者回滚全局事务

使用逻辑:

1.每个微服务要使用seata都需要一个undo_log表,并且数据库需要使用innodb引擎

-- 注意此处0.3.0+ 增加唯一索引 ux_undo_log
CREATE TABLE `undo_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(100) NOT NULL,
  `context` varchar(128) NOT NULL,
  `rollback_info` longblob NOT NULL,
  `log_status` int(11) NOT NULL,
  `log_created` datetime NOT NULL,
  `log_modified` datetime NOT NULL,
  `ext` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

2.在common模块里导入依赖

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
        </dependency>

注意到seata-all的版本

 3.下载seata-server的版本必须和seata-all指定的版本一致

4.更改配置文件:conf/registry.conf为nacos注册中心

 5.在nacos注册中心看到seata服务器打开了就行

6.添加一个全局事务注解,@Transactional标在每一个分支事务

7.每一个想要使用分布式事务的微服务都要使用seataSourceProxy代理自己的数据源 ,该配置放置在所有想参与到分布式事务的服务里,这里我放在了ware和order里

package com.wuyimin.gulimall.order.config;

/**
 * @ Author wuyimin
 * @ Date 2021/8/29-11:55
 * @ Description Seata代理数据源配置
 */
@Configuration
public class MySeataConfig {
    @Autowired
    DataSourceProperties dataSourceProperties;

    @Bean
    public DataSource dataSource(DataSourceProperties dataSourceProperties) {

        HikariDataSource dataSource = dataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
        if (StringUtils.hasText(dataSourceProperties.getName())) {
            dataSource.setPoolName(dataSourceProperties.getName());
        }
        return new DataSourceProxy(dataSource);
    }
}

8.每一个微服务还需要file.config和registry.config两个文件,需要在file.config里手动添加server配置,主要 vgroupMapping.gulimall-ware-fescar-service-group = "default"需要更改成每一个模块名字,1.3以后这里vgroupMapping改成了驼峰命名要注意

service {
  #vgroup->rgroup
  vgroupMapping.gulimall-ware-fescar-service-group = "default"
  #only support single node
  default.grouplist = "127.0.0.1:8091"
  #degrade current not support
  enableDegrade = false
  #disable
  disable = false
  #unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent
  max.commit.retry.timeout = "-1"
  max.rollback.retry.timeout = "-1"
}

此外还需要配置yml文件

原文链接

9.模拟事务失败

 10.测试成功结果,仓库锁定操作也跟着回滚

最终一致性库存解锁逻辑

以上操作默认使用的是AT模式(基于ACID),是基于2PC的二阶提交的演变,并不适合高并发场景,同时Seata支持的TCC模式也不适合,Seata比较适合于非高并发的场景比如后台管理系统里的保存操作。为了保证高并发,库存服务自己回滚,可以发消息给库存服务。库存服务本身也可以使用自动解锁模式,这些都可以使用消息队列来实现

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值