事务:逻辑上的一组操作,要么都成功要么都失败
事务的四个特性:ACID 原子性,一致性,隔离性,持久性
事务的隔离级别:
读未提交:产生脏读
读已提交:不可重复读
可重复读:幻读(mysql默认)
序列化读:性能最低
传播行为(7个)
七种传播行为:
REQUIRED 支持当前事务,如果不存在,就新建一个
SUPPORTS 支持当前事务,如果不存在,就不使用事务
MANDATORY 支持当前事务,如果不存在,抛出异常
REQUIRES_NEW 如果有事务存在,挂起当前事务,创建一个新的事务
NOT_SUPPORTED 以非事务方式运行,如果有事务存在,挂起当前事务
NEVER 以非事务方式运行,如果有事务存在,抛出异常
NESTED 如果当前事务存在,则嵌套事务执行(嵌套式事务)
这七种事务传播机制最常用的就两种:
REQUIRED:一个事务,要么成功,要么失败
REQUIRES_NEW:两个不同事务,彼此之间没有关系。一个事务失败了不影响另一个事务
回滚策略
默认情况下的回滚策略:
- 运行时异常:不受检异常,没有强制要求try-catch,都会回滚。例如:ArrayOutOfIndex,OutofMemory,NullPointException
- 编译时异常:受检异常,必须处理,要么try-catch要么throws,都不回滚。例如:FileNotFoundException
只读事务
如果一个方法标记为readOnly=true事务,则代表该方法只能查询,不能增删改。readOnly默认为false
给商品新增的事务标记为只读事务:
分布式事务
seata
前提:
- 启动seata-service服务
- 数据库表中有undo_log表
1.引入依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-seata</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>0.8.0</version>
</dependency>
2.配置文件
registry.conf:配置注册中心和配置中心,默认是file。
该文件包含两部分配置:
- 注册中心
- 配置中心
registry { # 注册中心配置
# 可选项:file 、nacos 、eureka、redis、zk
type = "file"
nacos {
serverAddr = "127.0.0.1:8848"
namespace = "public"
cluster = "default"
}
eureka {
serviceUrl = "http://localhost:1001/eureka"
application = "default"
weight = "1"
}
redis {
serverAddr = "localhost:6381"
db = "0"
}
zk {
cluster = "default"
serverAddr = "127.0.0.1:2181"
session.timeout = 6000
connect.timeout = 2000
}
file {
name = "file.conf"
}
}
config { # 配置中心
# 可选项:file、nacos 、apollo、zk
type = "file" # 指向file配置中心,也可以指向nacos等其他注册中心
nacos {
serverAddr = "localhost"
namespace = "public"
cluster = "default"
}
apollo {
app.id = "fescar-server"
apollo.meta = "http://192.168.1.204:8801"
}
zk {
serverAddr = "127.0.0.1:2181"
session.timeout = 6000
connect.timeout = 2000
}
file {
name = "file.conf" # 通过file.conf配置seata参数,指向第二个配置文件
}
}
file.conf:seata工作规则信息
该文件的命名取决于registry.conf配置中心的配置
transport {
# tcp udt unix-domain-socket
type = "TCP"
#NIO NATIVE
server = "NIO"
#enable heartbeat
heartbeat = true
#thread factory for netty
thread-factory {
boss-thread-prefix = "NettyBoss"
worker-thread-prefix = "NettyServerNIOWorker"
server-executor-thread-prefix = "NettyServerBizHandler"
share-boss-worker = false
client-selector-thread-prefix = "NettyClientSelector"
client-selector-thread-size = 1
client-worker-thread-prefix = "NettyClientWorkerThread"
# netty boss thread size,will not be used for UDT
boss-thread-size = 1
#auto default pin or 8
worker-thread-size = 8
}
}
## transaction log store
store {
## store mode: file、db
mode = "db" ## 改成db模式
## database store db相关参数根据个人环境设置
db {
driver_class = "com.mysql.jdbc.Driver"
url = "jdbc:mysql://172.16.116.100:3306/guli-pms"
user = "root"
password = "root"
}
}
service {
#vgroup->rgroup
# 根据工程的服务名修改
vgroup_mapping.pms-service-fescar-service-group = "default"
#only support single node
default.grouplist = "127.0.0.1:8091"
#degrade current not support
enableDegrade = false
#disable
disable = false
}
client {
async.commit.buffer.limit = 10000
lock {
retry.internal = 10
retry.times = 30
}
}
DataSourceConfig:配置代理数据源实现分支事务,如果没有注入,事务无法成功回滚
每一个微服务原来自己的数据源都必须使用DataSourceProxy代理,这样seata才能掌控所有事务。
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DruidDataSource druidDataSource() {
return new DruidDataSource();
}
/**
* 需要将 DataSourceProxy 设置为主数据源,否则事务无法回滚
*
* @param druidDataSource The DruidDataSource
* @return The default datasource
*/
@Primary
@Bean("dataSource")
public DataSource dataSource(DruidDataSource druidDataSource) {
return new DataSourceProxy(druidDataSource);
}
}
3.注解 @GlobalTransactional @Transactional
主业务方法添加全局事务:@GlobalTransactional
分支业务方法添加本地事务注解:@Transactional