什么是事务:
事务就是保证数据库交易可靠性的一种机制
事务的四种特性
A 原子性: 事务操作要么全部成功,要么全部失败
C 一致性: 一个事务在执行前后数据库的状态都必须一致
I 隔离型: 事务相互隔离,不能被干扰
D 持久性:对数据库的操作成功以后,永久有效
为什么要使用分布式事务:
因为事务无法满足分布式下的数据一致性,所以出现了分布式事务
什么是分布式事务:
分布式事务就是为了解决多数据库下实现数据一致性
首先我们要知道CAP定理 CAP定理也是三者不可兼得
C 一致性 在分布式系统中数据必须都是同一样的值
A 可用性 在集群中一部分节点故障后是否还可以相应客户端
P 分区容错性 区间通信可能会失败
一般来说,分区容错无法避免,所以认为P一定成立,所以要么AP,要么CP
什么是seata:
seata是一个alibaba开发出得分布式事务解决方法,支持了AT,TCC,SAGA,XA事务模式
我使用是使用了AT强一致性二阶段提交的事务模式
seata服务端:
**使用seata请先下载服务端 我自己使用的是1.3.0 注册中心是nacos1.3.1
修改配置文件application.properties把注释掉的这个打开修改成对应的用户密码
然后把conf下的nacos-mysql.sql文件在mysql中执行 修改成对应的数据库名
### If use MySQL as datasource:
spring.datasource.platform=mysql
### Count of DB:
db.num=1
### Connect URL of DB:
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user=root
db.password=root
然后单机启动 ./startup.sh -m standalone
访问 localhost:8848/nacos就可以看到nacos启动成功
在1.0.0以后一些配置文件都不在下载的包里,要自行下载
config.txt进去就可以看到,.sh文件下级目录可以找到
.sh下载过放到conf下,config.txt放到conf同级的目录
然后修改txt文件,将.sh执行,把配置加载到nacos中,启动时要加上端口号
在nacos中就可以看到加载了进来
修改配置文件的数据库配置,然后这里的组要跟client中的组对应,先记下来
要在需要的每个数据库都添加undo_log表
修改file.conf和registry.conf文件
改成db,修改数据库信息
把注册中心修改nacos
下边的配置使用file文件就可以
以db方式启动
在bin目录下启动seata-server.sh -m db
可以看到seata注册到了nacos
最后文件是这样子的
seata客户端:
<dependency> //springboot包
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.6.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--spring cloud Hoxton.SR7-->
<dependency> //springcloud包
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR7</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--spring cloud alibaba 2.1.1.RELEASE-->
<dependency> //alibabacloud包
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>//seata包
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<version>2.2.1.RELEASE</version>
<exclusions>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>//nacos注册中心
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
yml配置文件:
seata:
enabled: true
application-id: system-exporta //一般可以用服务名称 要唯一
tx-service-group: my_group //要和服务端的组对应
config:
type: nacos
nacos:
namespace:
server-addr: localhost:8848
group: SEATA_GROUP
username: nacos
password: nacos
registry:
type: nacos
nacos:
application: seata-server
server-addr: localhost:8848
group: SEATA_GROUP
namespace:
username: nacos
password: nacos
service:
vgroup-mapping:
my_group: default 和服务端的对应
在启动类上要把自动的数据源关闭,然后自己定义数据原
mybatis和mybatis-plus配置不同,千万别搞错
mybatis:
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource() {
return new DruidDataSource();
}
@Bean
public DataSourceProxy dataSourceProxy(DataSource druidDataSource) {
return new DataSourceProxy(druidDataSource);
}
@Bean
public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSourceProxy);
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
bean.setMapperLocations(resolver.getResources(“你的xml文件位置”));
return bean.getObject();
}
mybatis-plus:
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource() {
return new DruidDataSource();
}
@Bean
@Primary
public DataSourceProxy dataSourceProxy(DataSource druidDataSource) {
return new DataSourceProxy(druidDataSource);
}
@Bean
public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSourceProxy);
bean.setTypeAliasesPackage("包路径"); //比如 com.xxx.xxx.mapper
MybatisConfiguration configuration = new MybatisConfiguration();
configuration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);
configuration.setJdbcTypeForNull(JdbcType.NULL);
bean.setConfiguration(configuration);
return bean.getObject();
}
**setTransactionFactory(new SpringManagedTransactionFactory())
这个配置是一个大坑,千万别加,找了一个星期为什么不回回滚
其余服务配置相同
然后使用就是在方法上加上一个
@GlobalTransactional(name = “save”,rollbackFor = Throwable.class)注解
被调用方不用加 加上注解就是一个TM,一个服务可以同时是一个TM和RM
name自己起,rollbackFor应该是接收到什么类型错误回滚
如果你在A服务的方法加了这个注解,然后调用了B服务,B服务报错的话是不会回滚的,必须要这个注解捕获到错误才可以回滚,可以在B方法抛出异常抛给A,这样就可以回滚**
启动一个服务:可以在服务端看到RM注册了进来
TC (Transaction Coordinator) - 事务协调者
维护全局和分支事务的状态,驱动全局事务提交或回滚。
TM (Transaction Manager) - 事务管理器
定义全局事务的范围:开始全局事务、提交或回滚全局事务。
RM (Resource Manager) - 资源管理器
管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
RM通俗一点可以说是事务参与者
TC就是事务协调者
TM可以算是事务发起者
只是自己理解
启动第二个服务
在调用方打上断点:
参与的两张表都是空数据
断点后发现数据插了进去
然后放开断点,1/0会报错
idea打印回滚信息
服务端显示回滚成功
然后数据表里的数据会被回滚
完成