1、事务的基本性质
数据库事务的几个特性:
原子性(Atomicity )、一致性( Consistency )、隔离性或独立性( Isolation) 和持久性(Durabilily),简称就是 ACID;
原子性:
一系列的操作整体不可拆分,要么同时成功,要么同时失败
一致性:
数据在事务的前后,业务整体一致。
转账。A:1000;B:1000; 转 200 事务成功; A:800 B:1200
隔离性:
事务之间互相隔离。
持久性:
一旦事务成功,数据一定会落盘在数据库。
2、事务的隔离级别
READ UNCOMMITTED(读未提交)
该隔离级别的事务会读到其它未提交事务的数据,此现象也称之为脏读。
READ COMMITTED(读提交)
一个事务可以读取另一个已提交的事务,多次读取会造成不一样的结果,此现象称为不可重 复读问题,Oracle 和 SQL Server 的默认隔离级别。
REPEATABLE READ(可重复读)
该隔离级别是 MySQL 默认的隔离级别,在同一个事务里,select 的结果是事务开始时时间 点的状态,因此,同样的 select 操作读到的结果会是一致的,但是,会有幻读现象。MySQL 的 InnoDB 引擎可以通过 next-key locks 机制(参考下文"行锁的算法"一节)来避免幻读。
SERIALIZABLE(序列化)
在该隔离级别下事务都是串行顺序执行的,MySQL 数据库的 InnoDB 引擎会给读操作隐式 加一把读共享锁,从而避免了脏读、不可重读复读和幻读问题。
3、事务的传播行为
1、PROPAGATION_REQUIRED:
如果当前没有事务,就创建一个新事务,如果当前存在事务, 就加入该事务,该设置是最常用的设置。
2、PROPAGATION_SUPPORTS:
支持当前事务,如果当前存在事务,就加入该事务,如果当 前不存在事务,就以非事务执行。
3、PROPAGATION_MANDATORY:
支持当前事务,如果当前存在事务,就加入该事务,如果 当前不存在事务,就抛出异常。
4、PROPAGATION_REQUIRES_NEW:
创建新事务,无论当前存不存在事务,都创建新事务。
5、PROPAGATION_NOT_SUPPORTED:
以非事务方式执行操作,如果当前存在事务,就把当 前事务挂起。
6、PROPAGATION_NEVER:
以非事务方式执行,如果当前存在事务,则抛出异常。
7、PROPAGATION_NESTED:
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务, 则执行与 PROPAGATION_REQUIRED 类似的操作。
事务失效和解决办法(看注释内容)
解决:
0)、导入 spring-boot-starter-aop
1)、@EnableTransactionManagement(proxyTargetClass = true)
2)、@EnableAspectJAutoProxy(exposeProxy=true)
3)、AopContext.currentProxy() 调用方法
/**
* 类本类调用本地事务,注意
* 1)
* <dependency>
* <groupId>org.springframework.boot</groupId>
* <artifactId>spring-boot-starter-aop</artifactId>
* </dependency>
* 2) @EnableAspectJAutoProxy(exposeProxy = true)
* 3) 使用AopContext.currentProxy()代理业务方法,处理类本类的调用事务失效问题,或者注入ioc别的service的事务方法
* OrderServiceImpl orderService = (OrderServiceImpl) AopContext.currentProxy();
*/
@Transactional
public void a(){
OrderServiceImpl orderService = (OrderServiceImpl) AopContext.currentProxy();
orderService.b();
orderService.c();
}
@Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRED)
public void b(){}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void c(){
}
分布式事务
概念
分布式系统经常出现的异常 机器宕机、网络异常、消息丢失、消息乱序、数据错误、不可靠的 TCP、存储数据丢失…
cap理论
c:一致性
在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访 问同一份最新的数据副本)
a:可用性
在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据 更新具备高可用性)
p:分区容错性
大多数分布式系统都分布在多个子网络。每个子网络就叫做一个区(partition)。 分区容错的意思是,区间通信可能失败。比如,一台服务器放在中国,另一台服务 器放在美国,这就是两个区,它们之间可能无法通信。
CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾
Raft算法原理
https://thesecretlivesofdata.com/raft/
BASE理论
是对 CAP 理论的延伸,思想是即使无法做到强一致性(CAP 的一致性就是强一致性),但可 以采用适当的采取弱一致性,即最终一致性
Seata分布式事务
创建 UNDO_LOG 表
-- 注意此处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;
启动服务
从 https://github.com/seata/seata/releases,下载服务器软件包,将其解压缩。
seata注册到注册中心
修改配置文件registry.conf文件,type = "nacos"
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa 指定注册中心
type = "nacos"
nacos {
serverAddr = "localhost:8848"
namespace = "public"
cluster = "default"
}
}
每个微服务下的resources下放上registry.conf
和file.conf
文件
需要注意的是file配置中的service.vgroup_mapping这个配置改成${当前微服务名字}-fescar-service-group
例如:
#vgroup->rgroup
vgroup_mapping.gulimall-order-fescar-service-group = "default"
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<exclusions>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>0.9.0</version>
</dependency>
写数据源配置类(使用代理对象)
@Configuration
public class MySeataConfig {
@Autowired
DataSourceProperties dataSourceProperties;
@Bean
DataSource dataSource(DataSourceProperties properties){
HikariDataSource dataSource = properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
if (StringUtils.hasText(properties.getName())) {
dataSource.setPoolName(properties.getName());
}
return new DataSourceProxy(dataSource);
}
}
使用注解@GlobalTransactional
@GlobalTransactional
注解写到总事务方法上面