事务
(1)事务特性
①原子性(Atomicity):事务的中的操作要么全部成功要么全部失败
②一致性(Consistency):事务必须使数据库从一个一致性状态到另一个一致性状态,例:A和B一起来有5000块,无论两者之间如何转账最后总量都必须为5000
③隔离性(Isolation):同时开启的两个事务之间互不干扰
④持久性(Durability):指一个事务一旦提交,那么对数据库的保存都是以永久的
(2)事务并发问题
①脏读:一个事务读取过程中,读取到了另一个事务未提交的数据
②不可重复读:一个事务内多次查询同一数据却返回了不同的值,这是因为在查询间隔,数据被另一个数据修改并提交了.
③虚读|幻读:例事务A删除了表中的全部数据的同时事务B在表中又插了一条数据,当操作事务A 的用户再查看时发现库里还有条数据,就像产生了幻觉一样
注: 幻读和不可重复读都是读取了另一条已经提交的事务,不同的是不可重复读查 询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)
(3)事务的隔离级别
①读未提交(read uncommited):最低级别,事务并发(①②③)都可能发生
②读已提交(read commited):可避免脏读的发生,事务并发(②③)可能发生
③可重复度(repeatable read):可避免脏读和不可重复读,事务并发(③)可能发生
④串行化(serialiable):事务排队一个接着一个进行,可避免事务并发问题
spring封装了事务管理代码
1.事务操作
(1) 事务操作对象:因为不同的平台操作事务的代码不相同,所以spring提供了一个接口
(2) PlatFormTransactionManager:接口
根据不同的环境使用不同的实现类:DataSourceTransactionManager,Hiber bateManager
注:在spring中事务的管理最重要的就是TransactionManager对象
(3) spring管理事务的属性介绍
①事务的隔离级别
②是否只读
③事务的传播行为
2.spring管理事务的方式
(1)编码式(了解)
(2)xml配置(aop)
①导包(4{core,beans,context,expression}+2(logging,log4j)
+2(aop+aspects)+2{weving+aopalliance}+spring-tx )
②导入约束
③配置详解
其他代码同模板初体验
aop:advisor大多用于事务管理。
aop:aspect大多用于日志、缓存。
(3)注解配置(aop)
①导包(同配置详解)
②导入约束(同配置详解)
③开启注解管理事务
④使用注解
3.事务传播特性
a方法头上标注了@Transactional注解,b方法头上页标注了@Transactional注解,且b方法中调用a方法这样就出现了事务的传播
Spring中通过Propagation来设置事务的传播属性的,在这个属性中提供了我们其中关于事务传播的特性:
- REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
- SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
- MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
- REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
- NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
- NESTED:支持当前事务,新增Savepoint点,与当前事务同步提交或回滚。
springboot中的事务
springboot事务的自动配置
TransactionAutoConfiguration
springboot事务的坑
在一个方法里面,编写两个方法,内部调用的时候,会导致内部方法的事务设置失效,原因是没有用到内部调用方法代理对象的缘故,
事务使用代理对象控制,对事务的设置都是对代理对象,而内部调用等于直接调用方法绕过了代理步骤。
解决:使用代理对象来调用事务方法
1.引入依赖,以来内部帮我们依赖了aspectj
2.开启aspectj动态代理功能,以后所有的代理都是aspectj创建的,即使没有接口也可以创建动态代理。
exposeProxy = true对外暴漏代理功能
@EnableAspectJAutoProxy(exposeProxy = true)
3.本类内调使用代理对象
Person person = (Person)AopContext.currentProxy();
person.b()
分布式事务
分布式系统经常出现的异常
机器宕机,网络异常,消息丢失,消息乱序数,据错误,不可靠的tcp,存储数据丢失
分布式事务是企业集成中的一个技术难点,也是每一个分布式系统架构中都会涉及到的一个东西,特别是在微服务架构中,几乎可以说是无法避免。
CAP定理与BASE理论
CAP定理:指的是在一个分布式系统中
一致性和可用性是冲突的,一致性是指若三个db都要同时存储一个数据,此时一台服务器由于网络原因无法存储数据。
若我们想要保持所有db的可用性,就会导致在访问时访问到的数据与其他两台db不一致。
若想保证数据一致性,在负载均衡时就要避开未正确存储数据的服务器。
分布式系统中实现一致性的raft算法
原理动画展示 raft算法
大致流程 : 存储请求,各db自旋时间,先旋完为候选,候选db请求选举操作,其他未自旋结束db选举领导,得票超一半db被选为领导,其他db为随从,选举与保持心跳连接,领导写入日志,心跳连接通知随从写入日志,各随从写入成功通知领导,若大多数成功,领导db写入数据并通知随从写入,随从写数据入。若在上述途中网络问题发生分区错误,会重新选取领导,若网络回复会自动跟随现有领导,写入日志并写入数据
面临的问题
对于大型互联网应用的场景,主机众多部署分散,而且现在的集群规模越来越大,所有节点故障,网络故障是常态,而且要保证服务可用性达到99.99%,既保证P和A,舍弃C
BASE理论 (基本可用)
对CAP理论的延伸,思想是即使无法做到强一致性(CAP的一致性就是强一致性),但可以采用适度的弱一致性,即最终一致性
强一致性,弱一致性,最终一致性
多进程并发访问,更新后的数据在不同进程如何获取的不同策略,强一致性:要求更新后的数据立刻能被看到,弱一致性能够容忍后续的部分或者全部访问不到,最终一致性在经过一段时间后要求能够访问更新后的数据
分布式事务集中解决方案
2PC/CA模式(了解)
柔性事务
刚性事务遵循ACID原则,强一致性,柔性事务遵循BASE理论弱一致性,允许一定时间内,不同节点数据不一致,但要求最终一致。
TCC事务补偿性方案(3pc的手动版)
最大努力通知型方案
可靠消息 + 最终一致性解决方案(异步确保类型)
解决分布式事务
springcould alibaba seata(分布式事务中间件)
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
术语
TC - 事务协调者: 维护全局和分支事务的状态,驱动全局事务提交或回滚。
TM - 事务管理器: 定义全局事务的范围:开始全局事务、提交或回滚全局事务。
RM - 资源管理器: 管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
AT模式解决分布式事务(不适合高并发业务)
两阶段提交协议的演变:
一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
二阶段:
-提交异步化,非常快速地完成。
-回滚通过一阶段的回滚日志进行反向补偿。
缺点
内部添加了各种锁,在高并发环境下性能太差
模拟业务:订单服务创建订单会调用库存服务扣库存,若失败订单回滚,库存也需要回滚
解决流程图:
1.为每个微服务创建 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;
2.安装并启动服务(安装事务协调器)
从 https://github.com/seata/seata/releases,下载服务器软件包,将其解压缩。
在config/registry.conf文件下配置相关信息,注册中心类型及地址,seata服务器配置及配置文件存放方式这里使用file型
在config/file.conf文件(可以搬到nacos中),使用file型存放的配置文件
每个微服务都需:
将registry.conf和file.conf文件复制到resource中
file.conf 的 service.vgroup_mapping 配置必须和spring.application.name一致
使用seata-server.bat启动中间件
3.服务引入依赖
4.注入 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);
}
}
6.给分布式大事务的入口标注@GlobalTransactional
7.每个远程的小事物(分支事务)使用@Transactional
可靠消息+最终一致性解决分布式事务
模拟业务:订单服务创建订单会调用库存服务扣库存,若失败订单回滚,库存也需要回滚
详细参考 https://blog.csdn.net/qq_35419179/article/details/105735131
锁定库存时,库存工作单数据也应该记录对应库存被锁定的信息(锁定库存的商品id,订单id,锁定的数量,锁定状态
下单成功,锁定库存成功,其他业务调用失败导致订单回滚 : 将库存锁定工作单放入延时队列,
解锁库存:根据取出队列中库存工作单,根据工作单给指定的库存的某个商品增加库存数(回滚操作)
消息积压,重复,丢失等解决方案
1.消息丢失
2.消息重复
3.消息积压
转载:https://blog.csdn.net/qq_35419179/article/details/106152982