什么是事务:事务是指做一件事,要么全部都成功,要么全部都失败;
事务(ACID)的四大特性:
原子性:进行操作时,要么全部都操作成功,要么全部都操作失败,不存在操作一半的情况;
一致性:一致性是指操作数据的一致性,操作成功或者失败后,数据的总数不变;
隔离性(重点)隔离有四种隔离级别:1.读未提交--存在脏读问题;
2.读已提交--存在不可重复读问题;
3.可重复读--存在幻读问题(MySql默认级别,默认已解决幻读问题) 4.串行化--没有问题,但是效率低下;
持久性:数据操作完成后,保存在磁盘中,此时无论网络连接失败或者关机,都不会影响存在磁盘中的数据;
什么是脏读--数据在进行查询时,查询到其他未提交的数据,若其他未提交的数据操作失败,进行了回滚,此时该数据就不存在了,读到了不存在的数据--就是脏读;
如下图:
什么是不可重复读;比如用户在读取数据的时候,先读取到的数据为id=1,在进行读取时,此时id=10,被其他人修改了,读到了别人已提交的数据,导致了前后读的数据不一致了;
如下图:
什么是幻读;如果在刚开始读取的数据,id=1,过了几分钟之后,再去查寻,此时多了一个数据age=26;此时就出现了幻读的情况;
如下图:
串行化,效果是最好的,但是效率是最低的;
MySql默认的隔离级别是可重复读,底层解决了幻读的问题,用了mvcc(多版本并发控制)机制,实现了读写不冲突;每次读的时候但会有一个单独的副本,解决了幻读的问题;
MySql中的innodb是如何保证数据的原子性的?
nodoLog,进行对旧数据的备份,数据进行操作时,会把旧数据备份一份,若数据进行操作时发生的错误,则把数据进行回滚,此时就可以用到旧数据了;
MySql中的innodn是如何保证数据的持久性的?
redolog,进行对新数据的备份,数据在完成操作后,会把新数据备份一份,若事务在进行操作时出现异常了,导致服务宕机了,此时就可以用到备份的新数据,系统可以根据redolog的内容,恢复到之前的数据;
在项目中集成SeaTa,分布式事务@GlobalTransactional
原理图;
SeaTa分为三部分;
事务协调器(TC):类似于Eurkea,事务管理器注册进来,会给一个XID返回,
事务管理器(TM):简单理解为前端发送的请求的地方为事务管理器;通过XID传输给资源管理器;
资源管理器(RM): 微服务调用的服务,为资源管理器;
事务管理器使用步骤:
1.导入依赖;
2.配置yml;
3.启动类排除数据源;
4.数据源代理类;
5.两个配置文件(写在Resources下面):file和registry;
5.注释MyBatisPlus中的事务管理器;
6.方法上加注解@GlobalTransactional;
1.导入依赖;
<!--seata相关依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-seata</artifactId>
<version>2.1.0.RELEASE</version>
<exclusions>
<exclusion>
<artifactId>seata-all</artifactId>
<groupId>io.seata</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>1.1.0</version>
</dependency>
2.配置yml;
spring:
#seata相关配置
cloud:
alibaba:
seata:
tx-service-group: fsp_tx_group #这里和file.conf中事务组名一样
3.启动类排除数据源;
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableEurekaClient
@EnableFeignClients(value = "cn.itsource.feignauth")
public class hrm_system_server_1040App {
public static void main(String[] args){
SpringApplication.run(hrm_system_server_1040App.class, args);
}
}
4.数据源代理类;
/**
* 数据源代理
*/
@Configuration
public class DataSourceConfiguration {
//mapper.xml路径
@Value("${mybatis-plus.mapper-locations}")
private String mapperLocations;
//手动配置bean
@Bean
@ConfigurationProperties("spring.datasource")
public DataSource druidDataSource(){
return new DruidDataSource();
}
@Bean
public DataSourceProxy dataSource() {
return new DataSourceProxy(druidDataSource());
}
@Bean
public MybatisSqlSessionFactoryBean sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
//处理MybatisPlus
MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
factory.setDataSource(dataSourceProxy);
factory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
//事务管理工厂
factory.setTransactionFactory(new SpringManagedTransactionFactory());
org.apache.ibatis.session.Configuration configuration = factory.getObject().getConfiguration();
configuration.setMapUnderscoreToCamelCase(true);//开启驼峰大小写自动转换
factory.setConfiguration(configuration);
return factory;
}
}
5.两个配置文件(写在Resources下面):file和registry;
file.config
transport {
# tcp udt unix-domain-socket
type = "TCP"
#NIO NATIVE
server = "NIO"
#enable heartbeat
heartbeat = true
# the client batch send request enable
enableClientBatchSendRequest = true
#thread factory for netty
threadFactory {
bossThreadPrefix = "NettyBoss"
workerThreadPrefix = "NettyServerNIOWorker"
serverExecutorThread-prefix = "NettyServerBizHandler"
shareBossWorker = false
clientSelectorThreadPrefix = "NettyClientSelector"
clientSelectorThreadSize = 1
clientWorkerThreadPrefix = "NettyClientWorkerThread"
# netty boss thread size,will not be used for UDT
bossThreadSize = 1
#auto default pin or 8
workerThreadSize = "default"
}
shutdown {
# when destroy server, wait seconds
wait = 3
}
serialization = "seata"
compressor = "none"
}
service {
#transaction service group mapping
vgroupMapping.fsp_tx_group = "default"
#only support when registry.type=file, please don't set multiple addresses
default.grouplist = "127.0.0.1:8091"
#degrade, current not support
enableDegrade = false
#disable seata
disableGlobalTransaction = false
}
client {
rm {
asyncCommitBufferLimit = 10000
lock {
retryInterval = 10
retryTimes = 30
retryPolicyBranchRollbackOnConflict = true
}
reportRetryCount = 5
tableMetaCheckEnable = false
reportSuccessEnable = false
}
tm {
commitRetryCount = 5
rollbackRetryCount = 5
}
undo {
dataValidation = true
logSerialization = "jackson"
logTable = "undo_log"
}
log {
exceptionRate = 100
}
}
registry.config:
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "file"
nacos {
serverAddr = "localhost"
namespace = ""
cluster = "default"
}
eureka {
serviceUrl = "http://localhost:8761/eureka"
application = "default"
weight = "1"
}
redis {
serverAddr = "localhost:6379"
db = "0"
password = ""
cluster = "default"
timeout = "0"
}
zk {
cluster = "default"
serverAddr = "127.0.0.1:2181"
session.timeout = 6000
connect.timeout = 2000
username = ""
password = ""
}
consul {
cluster = "default"
serverAddr = "127.0.0.1:8500"
}
etcd3 {
cluster = "default"
serverAddr = "http://localhost:2379"
}
sofa {
serverAddr = "127.0.0.1:9603"
application = "default"
region = "DEFAULT_ZONE"
datacenter = "DefaultDataCenter"
cluster = "default"
group = "SEATA_GROUP"
addressWaitTime = "3000"
}
file {
name = "file.conf"
}
}
config {
# file、nacos 、apollo、zk、consul、etcd3、springCloudConfig
type = "file"
nacos {
serverAddr = "localhost"
namespace = ""
group = "SEATA_GROUP"
}
consul {
serverAddr = "127.0.0.1:8500"
}
apollo {
app.id = "seata-server"
apollo.meta = "http://192.168.1.204:8801"
namespace = "application"
}
zk {
serverAddr = "127.0.0.1:2181"
session.timeout = 6000
connect.timeout = 2000
username = ""
password = ""
}
etcd3 {
serverAddr = "http://localhost:2379"
}
file {
name = "file.conf"
}
}
5.注释MyBatisPlus中的事务管理器;
@Configuration
@MapperScan("cn.itsource.mapper") //mapper接口扫描
//@EnableTransactionManagement //事务管理 因为集成了分布式事务,所以此事务管理需要注释
public class MybatisPlusConfig {
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
6.方法上加注解@GlobalTransactional;
@GlobalTransactional//开启seata全局事务
public JSONResult register(RegisterDto dto) {
此时事务管理器就集成完毕了,此时资源管理器和事务管理器一样的,但是不用加方法上加注解@GlobalTransactional;
Seata服务和redis一样都是要启动的,注意用管理员身份启动;