本地事务
只能控制自己的回滚,控制不了其他服务的回滚。
一般用SpringBoot的@Transactional()
但是有一个坑,当你设置了事务传播行为,在同一个对象内事务方法是失效的,因为事务是基于代理的,同对象动态代理都是同一个导致不能新建新的动态代理事务就设置失败了
失败案例:
解决方案:引用aop-starter
后,使用aspectJ
,开启AspectJ动态代理
,原来默认使用的是jdk动态代理。
主启动添加@EnableAspectJAutoProxy(exposeProxy=true)
后,就取代了jdk动态代理。它没有接口也可以创建动态代理。设置true是为了对外暴露代理对象。
AopContext.currentProxy()
然后强转,就是当前代理对象。
public void a() {
//调用AOP代理对象的b方法执行事务切面进行事务增强
((AService) AopContext.currentProxy()).b();
}
分布式事务
问题产生最大原因是网络问题。因为网络抖动根本不知道是成功还是失败。
分布式CAP&BASE理论
CAP原则又称 CAP定理,指的是在一个分布式系统中
一致性( Consistency):
- 在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
可用性( Availability)
- 在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)
分区容错性( Partition tolerance)
- 大多数分布式系统都分布在多个子网络。每个子网络就叫做一个区(partition)。分区容错的意思是,区间通信可能失败。比如,一台服务器放在中国,另一台服务器放在美国,这就是两个区,它们之间可能无法通信。
CAP原则指的是这三个要素最多只能同时实现两点,不可能三者兼顾
AP
BASE 理论
是对CAP理论的延伸,思想是即使无法做到强一致性(CAP的一致性就是强一致性),但可以采用适当的采取弱一致性,即最终一致性。
基本可用(Basically Available)
- 基本可用是指分布式系统在出现故障的时候,允许损失部分可用性(例如响应时间、
功能上的可用性),允许损失部分可用性。需要注意的是,基本可用绝不等价于系 统不可用。 - 响应时间上的损失: 正常情况下搜索引擎需要在0.5秒之内返回给用户相应的
查询结果,但由于出现故障(比如系统部分机房发生断电或断网故障),查询 结果的响应时间增加到了1~2秒。 - 功能上的损失:购物网站在购物高峰(如双十一)时,为了保护系统的稳定性, 部分消费者可能会被引导到-个降级页面。
软状态( Soft State)
- 软状态是指允许系统存在中间状态,而该中间状态不会影响系统整体可用性。分布式存储中一般一份数据会有多个副本,允许不同副本同步的延时就是软状态的体现。mysql replication的异步复制也是一种体现。
最终一致性( Eventual Consistency)
- 最终一致性是指系统中的所有数据副本经过一定时间后,最终能够达到一致的状 态。弱致性和强一致性相反, 最终一致性是弱一致性的一种特殊情况。
强一致性、弱一致性、最终一致性
- 从客户端角度,多进程并发访问时,更新过的数据在不同进程如何获取的不同策略,决定了不同的一致性。对于关系型数据库,要求更新过的数据能被后续的访问都能看到,这是强一致性。如果能容忍后续的部分或者全部访问不到,则是弱一致性。如果经过一段时间后要求能访问到更新后的数据,则是最终一致性。
分布式事务常见解决方案
2PC模式
数据库支持的2PC [2 phase commit二阶提交],又叫做XATransactionso
MySQL从5.5版本开始支持,SQL Server 2005开始支持,Oracle7 开始支持。
其中,XA是一个两阶段提交协议,该协议分为以下两个阶段:
- 第一阶段:事务协调器要求每个涉及到事务的数据库预提交(precommit)此操作,并反映是否可以提交.
- 第二阶段:事务协调器要求每个数据库提交数据。其中,如果有任何一个数据库否决此次提交,那么所有数据库都会被要求回滚它们在此事务中的那部分信息。
Seata 分布式事务中间件
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
Seata术语
三组件的概念
Transaction Coordinator(TC):事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚。
Transaction Manager™:控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议。
Resource Manager(RM):控制分支事务,负责分支汪册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务提交或回滚。
-
TM向TC申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的XID;
-
XID在微服务调用链路的上下文中传播;
-
RM向TC汪册分支事务,将其纳入XID对应全局事务的管辖;
-
TM向TC发起针对XID的全局提交或回滚决议;
-
TC调度XID下管辖的全部分支事务完成提交或回请求。
AT模式:(不适合高并发场景,锁机制导致性能下降)
分为三阶段:
Seata使用
配置文件修改
要使用数据保存回滚日志就要在服务对应的数据库添加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,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
<!-- seata-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<version>2.1.0.RELEASE</version>
<exclusions>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>1.0.0</version> <!--版本对应下载的客户端-->
</dependency>
启动seata客户端必须先启动nacos客户端
spring:
cloud:
alibaba:
seata:
tx-service-group: zng #事务组名需要跟配置一样
将配置文件复制到资源目录下
代理数据源(1.3以后好像不用手动代理了)
package com.zng.springcloud.config;
import com.alibaba.druid.pool.DruidDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;
@Configuration
public class DataSourceProxyConfig {
//使用druid代理数据源
@Value("${mybatis.mapperLocations}")
private String mapperLocations;
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource(){
return new DruidDataSource();
}
@Bean
public DataSourceProxy dataSourceProxy(DataSource dataSource){
return new DataSourceProxy(dataSource);
}
@Bean
public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSourceProxy);
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
return sqlSessionFactoryBean.getObject();
}
}
//排除数据源自动配置,让seata管理数据源
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
在业务入口添加@GlobalTransactional
//分布式事务,全局事务。 小事务用@Transactional