- 事务的基本概念
- 本地事务
- Undo Log 如何保障事务的原子性?
- Redo Log如何保障事务的持久性?
- 事务的四个隔离级别
- 事务在项目中的应用(@transactional注解)
- @Transactional注解可以作用于哪些地方?
- 几种常见的失效场景
- 事务传播机制
- 注解实现原理
- 分布式事务
- 什么是分布式事务
- 典型场景
- 理论基础
- base定理
- 分布式事务分类
- 刚性事务
- 柔性事务
- 一图搞懂 分布式事务
事务是由一组 *** 作构成的可靠的独立的工作单元,事务具备ACID的特性,即原子性、一致性、隔离性和持久性。
本地事务大多数场景下,我们的应用都只需要 *** 作单一的数据库,这种情况下的事务称之为本地事务(Local Transaction)。本地事务的ACID特性是数据库直接提供支持。
以MySQL 的InnoDB 为例,介绍一下单一数据库的事务实现原理。
InnoDB 是通过 日志和锁 来保证的事务的 ACID特性,具体如下:
(1)通过数据库锁的机制,保障事务的隔离性;
(2)通过 Redo Log(重做日志)来,保障事务的持久性;
(3)通过 Undo Log (撤销日志)来,保障事务的原子性;
(4)通过 Undo Log (撤销日志)来,保障事务的一致性;
具体的方式为:在 *** 作任何数据之前,首先将数据备份到一个地方(这个存储数据备份的地方称为 Undo Log),然后进行数据的修改。如果出现了错误或者用户执行了 Rollback 语句,系统可以利用 Undo Log 中的备份将数据恢复到事务开始之前的状态。
Redo Log如何保障事务的持久性?具体的方式为:Redo Log 记录的是新数据的备份(和 Undo Log 相反)。在事务提交前,只要将 Redo Log 持久化即可,不需要将数据持久化。当系统崩溃时,虽然数据没有持久化,但是 Redo Log 已经持久化。系统可以根据 Redo Log 的内容,将所有数据恢复到崩溃之前的状态。
事务的四个隔离级别未提交读(READ UNCOMMITTED):所有事务都可以看到其他事务未提交的修改。一般很少使用;
提交读(READ COMMITTED):Oracle默认隔离级别,事务之间只能看到彼此已提交的变更修改;
可重复读(REPEATABLE READ):MySQL默认隔离级别,同一事务中的多次查询会看到相同的数据行;可以解决不可重复读,但可能出现幻读;
可串行化(SERIALIZABLE):最高的隔离级别,事务串行的执行,前一个事务执行完,后面的事务会执行。读取每条数据都会加锁,会导致大量的超时和锁争用问题;
在多个事务并发 *** 作时,数据库中会出现下面三种问题:脏读,幻读,不可重复读。
可以作用在接口、类、类方法
- 作用于类
- 当把@Transactional 注解放在类上时,表示所有该类的public方法都配置相同的事务属性信息。
- 作用于方法
- 当类配置了@Transactional,方法也配置了@Transactional,方法的事务会覆盖类的事务配置信息。
- 作用于接口
- 不推荐这种使用方法,因为一旦标注在Interface上并且配置了Spring AOP 使用CGLib动态代理,将会导致@Transactional注解失效
- @Transactional 应用在非 public 修饰的方法上
- @Transactional 注解属性propagation 设置错误
- @Transactional 注解属性 rollbackFor 设置错误
- 同一个类中方法调用,导致@Transactional失效
- 异常被你的catch“吃了”导致@Transactional失效
- 数据库引擎不支持事务
链接:@Transactional失效场景
- REQUIRED
内/外只要有报错,他俩会一起回滚。 只要内层方法报错抛出异常,即使外层有try-catch,该事务也会回滚!
内层不存在事务,外层存在事务,即加入外层的事务,不管内层,外层报错,都会回滚事务。 - REQUIRES_NEW
外层报错回滚,不影响内层。 内层报错回滚,外层try-catch内层的异常,外层不会回滚。
内层报错回滚,然后又会抛出异常,外层如果没有捕获处理内层抛出来的这个异常,外层还是会回滚的。 - 不常用的
- NESTED
- SUPPORTS
- MANDATORY
- NOT_SUPPORTED
- NEVER
链接:事务传播机制详解
- @Transactional是基于动态代理实现的,@Transactional注解实现原理中分析了实现方法,在bean初始化过程中,对含有@Transactional标注的bean实例创建代理对象,这里就存在一个spring扫描@Transactional注解信息的过程,不幸的是源码中体现,标注@Transactional的方法如果修饰符不是public,那么就默认方法的@Transactional信息为空,那么将不会对bean进行代理对象创建或者不会对方法进行代理调用
- @Transactional注解实现原理中,介绍了如何判定一个bean是否创建代理对象,大概逻辑是。根据spring创建好一个aop切点BeanFactoryTransactionAttributeSourceAdvisor实例,遍历当前bean的class的方法对象,判断方法上面的注解信息是否包含@Transactional,如果bean任何一个方法包含@Transactional注解信息,那么就是适配这个BeanFactoryTransactionAttributeSourceAdvisor切点。则需要创建代理对象,然后代理逻辑为我们管理事务开闭逻辑。
- spring源码中,在拦截bean的创建过程,寻找bean适配的切点时,运用到下面的方法,目的就是寻找方法上面的@Transactional信息,如果有,就表示切点BeanFactoryTransactionAttributeSourceAdvisor能够应用(canApply)到bean中。
- 方法调用时,被切面拦截的方法会被TransactionInterceptor拦截,进入到父类TransactionAspectSupport中的invokeWithinTransaction方法,该方法则负责调用发放事务的开启,提交以及回滚。
- 对于分布式系统而言,需要保证分布式系统中的数据一致性,保证数据在子系统中始终保持一致,避免业务出现问题。分布式系统中对数要么一起成功,要么一起失败,必须是一个整体性的事务。
- 分布式事务指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。
-
跨库事务
跨库事务指的是,一个应用某个功能需要 *** 作多个库,不同的库中存储不同的业务数据。 -
分库分表
通常一个库数据量比较大或者预期未来的数据量比较大,都会进行水平拆分,也就是分库分表。 -
微服务化
某个应用同时 *** 作了9个库,这样的应用业务逻辑必然非常复杂,对于开发人员是极大的挑战,应该拆分成不同的独立服务,以简化业务逻辑。拆分后,独立服务之间通过RPC框架来进行远程调用,实现彼此的通信。
分布式事务实现方案必须要考虑性能的问题,如果为了严格保证ACID特性,导致性能严重下降,那么对于一些要求快速响应的业务,是无法接受的。
理论基础- CAP定理是由加州大学伯克利分校Eric Brewer教授提出来的,他指出WEB服务无法同时满足一下3个属性:
- 一致性(Consistency) : 客户端知道一系列的 *** 作都会同时发生(生效)
- 可用性(Availability) : 每个 *** 作都必须以可预期的响应结束
- 分区容错性(Partition tolerance) : 即使出现单个组件无法可用, *** 作依然可以完成
数据一致性分为强一致性、弱一致性、最终一致性。
如果的确能像上面描述的那样时刻保证客户端看到的数据都是一致的,那么称之为强一致性。
如果允许存在中间状态,只要求经过一段时间后,数据最终是一致的,则称之为最终一致性。
此外,如果允许存在部分数据不一致,那么就称之为弱一致性。
base是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语的缩写。base基于CAP定理演化而来,核心思想是即时无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。
分布式事务分类主要分为柔性事务和刚性事务。
刚性事务刚性事务
通常无业务改造,强一致性,原生支持回滚/隔离性,低并发,适合短事务。
原则
刚性事务满足足CAP的CP理论
刚性事务指的是,要使分布式事务,达到像本地式事务一样,具备数据强一致性,从CAP来看,就是说,要达到CP状态。
XA 协议(2PC、JTA、JTS)、3PC,但由于同步阻塞,处理效率低,不适合大型网站分布式场景。
柔性事务指的是,不要求强一致性,而是要求最终一致性,允许有中间状态,也就是base理论,换句话说,就是AP状态。
与刚性事务相比,柔性事务的特点为:有业务改造,最终一致性,实现补偿接口,实现资源锁定接口,高并发,适合长事务。
柔性事务分为:
补偿型
异步确保型
最大努力通知型。
TCC/FMT、Saga(状态机模式、Aop模式)、本地事务消息、消息事务(半消息)
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)