一,事务基础
1,事务:业务要么同时成功要么同时失败。spring事务是基于数据库事务和AOP动态代理实现的。
2,四大特性ACID:原子性,一致性,隔离性,持久性
原子性:事务是一个原子操作,有一系列的动作组成。这些动作要么全部完成,要么全部不起作用
一致性:一旦事务动作完成,事务就被提交。数据和资源就处于一种满足业务规则的一致性状态
隔离性:可能有多个事务同时处理相同的数据,所以每个事务必须与其他事务隔离开来
持久性:一旦事务完成,无论发生什么系统错误,他的结果都不应该受到影响。
3,数据库事务四大隔离级别:读未提交,读已提交Oracal,可重复读mysql,串行化
4,并发事务问题:
脏读---------在并发事务中,一个事务(T1)正在修改数据,而另一个事务(T2)读取到了这个正在被修改还未提交的数据,就是脏数据。
不可重复读---------在并发事务中,一个事务(T1)期间内多次读取同一数据,由于另外的一个事务(T2)修改了该数据,导致第一个事务(T1)中两次读到的数据可能不一致。
幻读 ----------在并发事务中,一个事务(T1)读取了几行数据,而另一个事务(T2)插入了一些数据时,在随后的查询中,第一个事务就会多出一些原本不存在的记录。
5,传播行为propagation:required(mysql默认), supports,mandatory,not_supported,requries_new,never,nested
6,事务失效的场景:
一,访问权限不是public
在spring源码中,如果目标方法不是public,则TransationAttribute返回null,不支持事务
二,方法用final修饰或者statis修饰
因为在spring事务底层用了aop,用了jdk的动态代理或者cjlib的动态代理,会帮我们生成代理类,在代码中
实现事务的功能。
如果被final修饰了,那么在代理类中,无法重新改方法,添加事务功能。
三,直接调用内部方法
四,未被spring管理
没有添加spring注解,交给spring容器来管理
五,多线程调用
六,表不支持事务
如果表的引擎是myisam,那么它是不支持事务的。要想支持事务,要缓存innodb
七,事务没有开启
springboot项目事务是默认开启的,如果是spring项目,需要xml配置
八,事务的传播特性
如果事务的传播属性是never,这种类型的传播特性不支持事务
目前只有三种传播特性支持事务,required,requires_new, nested
九,自己吞了异常
使用了try…catch,开发者自己捕获了异常,又没有手动抛出
十,手动抛出了别的异常
如果抛出的异常不正确,事务也不会回滚
十一,robackFor自定义回滚异常类型不符合
分布式系统CAP定理和BASE理论
CPA定理
(最多只能同时保证两项) -------------------------cpa权衡(保PA,舍c)
一致性consistency --------all nodes see the same time data at the same time
数据在多个副本节点中保持一致。
可用性availability ----------服务一直可用,而且正常响应时间
系统对外提供的服务一直处于可用状态,在任何故障下,客户端都可以获取到服务端非错误的响应。
分区容错性partition tolerance -----------the system continues to operate despite
arbitrary message loss or failure of part of the system.
在分布式系统中遇到任何网络分区故障,系统仍然可用
网络分区:在分布式系统中,不同的节点会分布在不同的字网络中,在所有网络正常下,由于某些原因导致
这些子节点之间的网络出现故障,造成整个节点环境被分割成了不同的独立区域。
BASE理论
核心思想:舍弃强一致性,而取最终一致性(系统中的所有数据副本在经过一定
时间后,)。
二,为何要使用分布式事务
单块的系统是运行在同一个jvm进程中的,分布式系统的各个服务运行在不同的jvm进程中。
在分布式系统中,不同服务会部署在不同的服务器上。比如在订单服务中,一个下订单就会需要调用很多其他的服务
三,分布式事务解决方案
1,核心理论
分布式事务是指 事务的参与者、支持事务的服务器、资源管理器和事务管理局分别位于分布式系统的不同节点上。
例如大型电商中的下单场景会涉及到扣库存、减积分、优惠促销计算、订单id生成,通常库存、积分、促销、订单id生成策略都位于不同的数据库和表中,那么下单结果的成功与否,不仅取决于本地节点数据库操作,还依赖第三方服务执行的结果,而分布式事务就是要保证这些结果要么全部成功,要么全部失败,本质来说分布式事务就是为了保证不同数据库的数据一致性。
基于分布式系统的CAP定律,对于上述的情况 要么采用强一致性方案,要么采用弱一致性方案。
2,强一致性方案和弱一致性方案
强一致性方案:
通过第三方事务管理器来协调多个节点的事务性,达到同时成功或者同时失败。
具体实现方案:采用XA协议二阶段提交实现
缺点:全局事务的多个节点在进行事务提交确认的时候,由于网络通信延迟导致的阻塞,就会影响到所有节点的事务提交。而这个阻塞的过程也会影响到用户的请求线程,这对于用户体验及整体的性能影响非常大。
弱一致性方案:
针对强一致性引入的在一致性和性能上平衡的一个方案。简单来说,就是损失掉强一致性,数据会在某个时间段存在不一致,但是最终会达成一致,这样做的好处是提升了系统的性能。
基于redis事务
分布式消息队列-----------异步提交
基于TCC事务补偿机制--------------针对每一个操作都需要注册一个和其相对应的确认和补偿操作,分别有三个阶段
try----------------------资源监测和资源预留
confirm---------------try执行成功,confirm一定成功,如果失败,就需要引入重试机制或者人工处理
cancle----------------try执行失败,cancle(执行业务取消,预留的资源释放)
使用seata事务框架(提供了支持多种事务模型)
最大努力通知方案:
消息通知机制
消息校对机制
四,seata
1,组成
TC -----------------------全局的事务协调器 --------------------seata-server
TM ----------------------事务管理器----------------------@GlobalTransaction
RM ----------------------资源管理器
2,seata原理
tm请求tc开启全局事务。tc会生成一个xid作为全局事务的编号。XID,会在微服务调用链路中,保证将多个微服务的子事务关联在一起。
rm请求tc将本地事务注册到全局事务的分支事务,通过全局XID进行关联
tm请求tc告诉xid对应的全局事务提交还是回滚。
tc驱动rm们将xid对应自己的本地事务进行提交还是回滚。
3,执行流程
第一阶段,
业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。核心在于对业务sql解析,转换成undolog,并同时入库。
第二阶段,分布式事务操作成功,则tc通知rm异步删除undolog。
分布式事务操作失败,tm向tc发送回滚请求。通过XID和branch id找到对应的回滚日志记录,通过回滚记录生成反向的更新sql并执行,以完成分支的回滚。
4,seata优点
应用层基于sql解析实现了自动补偿,从而最大程度的降低了业务侵入性
将分布式事务中tc独立部署,负责事务的注册,回滚
通过全局锁实现了写隔离和读隔离
5,springboot整合seata
环境准备
安装seata server,修改配置文件
修改registry.conf ,指定注册中心-------------------- type=“nacos” serverAddr=“192.168.56.1:8848”
修改file.conf, vgroup_mapping.{application.name}-fescar-service-group=“default”
第一步,引入seata的依赖
com.alibaba.cloud
spring-cloud-starter-alibaba-seata
第二步,添加nacos配置中心seata配置,在项目yml文件中配置seata服务
nacos配置seata:参考https://github.com/seata/seata/tree/develop/script/config-center的config.tx文件并修改
第三步,使用AT模式需要在每个使用到分布式事务的数据库中创建undo_log回滚日志表
– 注意此处0.7.0+ 增加字段 context
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;
第四步,在主事务上添加@GlobalTransactional注解,开启全局全局事务
在分支事务上添加@Transactional注解
第五步,项目启动前,先启动seata server
6,配置seata DataSourceProxy代理数据源
springboot数据源:DataSourceConfiguration
注入 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);
}
}
file.conf 的 service.vgroup_mapping 配置必须和spring.application.name一致
在 org.springframework.cloud:spring-cloud-starter-alibaba-seata 的org.springframework.cloud.alibaba.seata.GlobalTransactionAutoConfiguration 类中,默认会使用 ${spring.application.name}-fescar-service-group作为服务名注册到 Seata Server上,如果和file.conf 中的配置不一致,会提示 no available server to connect错误
也可以通过配置 spring.cloud.alibaba.seata.tx-service-group修改后缀,但是必须和file.conf中的配置保持一致