一:事务(经典事务+分布式事务)
1.经典事务:是指传统的单机数据库事务,必须具备ACID原则
2.分布式事务:现在主流的系统架构都是SOA的架构,以及现在日渐火热的微服务架构中,应用及数据库都会采用分布式集群部署。于是就产生了跨数据源或者跨服务的事务,这就是分布式事务。
①:跨数据源:分库分表的不同微服务模块(各自的数据库)
②:跨服务:不同微服务模块(因为有可能不同模块用同一个数据库,因为spring的事务底层是使用的Spring AOP动态代理机制实现的,所以不同微服务之间,不能保证俩个服务spring的事务一致性)
二:经典事务
1.是指传统的单机数据库事务,必须具备ACID原则:
①.原子性(A)
所谓的原子性就是说,在整个事务中的所有操作,要么全部完成,要么全部不做,没有中间状态。对于事务在执行中发生错误,所有的操作都会被回滚,整个事务就像从没被执行过一样。
②.一致性(C)
事务的执行必须保证系统的一致性,就拿转账为例,A有500元,B有300元,如果在一个事务里A成功转给B50元,那么不管并发多少,不管发生什么,只要事务执行成功了,那么最后A账户一定是450元,B账户一定是350元。
③.隔离性(I)
所谓的隔离性就是说,事务与事务之间不会互相影响,一个事务的中间状态不会被其他事务感知。
④.持久性(D)
所谓的持久性,就是说一单事务完成了,那么事务对数据所做的变更就完全保存在了数据库中,即使发生停电,系统宕机也是如此。
2.正常的本地事务:spring框架提供了两种事务实现方式(单个方法进行写的操作)
①:编程式事务:在代码中进行事务控制。优点:精度高。缺点:代码耦合度高
②:声明式事务:通过@Transactional注解实现事务控制
3.7种事务传播类型(不同service方法之间的调用进行写的操作)
①:支持当前事务
REQUIRED (必须有)
含义:如果当前方法没有事务,新建一个事务,如果已经存在一个事务中,则加入到这个事务中。
SUPPORTS (可有可无)
含义:支持当前事务,如果当前没有事务,就以非事务方式执行
MANDATORY(强制)
含义:使用当前的事务,如果当前没有事务,就抛出异常。
②:不支持当前事务
REQUIRES_NEW
含义:新建事务,如果当前存在事务,把当前事务挂起。
NOT_SUPPORTED
含义:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
NEVER
含义: 以非事务方式执行,如果当前存在事务,则抛出异常。
③:NESTED
NESTED
含义: 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
4.至于案例,我这就不一一例举了,可以自己百度一下相关资源很多的,随便自己敲一下代码理解一下应该就差不多了。
5.总结:
①:ACID事务的原则
②:单库单个方法写的操作事务(编程式事务,声明式事务)
③:单库不同service之间的方法调用写的操作事务(声明式事务7种传播性)
A:atom C:consistency I:isolation D:durable
三: 分布式
1.什么是分布式事务
事务发生时,其中的参与者主要包含:应用服务、数据库。在经典事务中,无论是应用服务还是数据库都是单机部署,因此可以完全遵循ACID原则。但是现在主流的系统架构都是SOA的架构,以及现在日渐火热的微服务架构中,应用及数据库都会采用分布式集群部署。于是就产生了跨数据源或者跨服务的事务,这就是分布式事务。
2.解决分布式事务的思路(cap定理,base理论)
①:在分布式系统中,就不得不面对CAP的定理
a.Consistency(一致性)
b.Availability(可用性)
c.Partition tolerance(分区容忍性)
具体地讲在分布式系统中,在任何数据库设计中,一个Web应用至多只能同时支持上面的两个属性。显然,任何横向扩展策略都要依赖于数据分区。因此,设计人员必须在一致性与可用性之间做出选择,而面对高并发的互联网行业,高可用显然会比一致性要重要的多,但是一致性也是不可抛弃的,如何解决这一矛盾?
②:这个时候,大神们有总结出了BASE理论:
a.Basically Available(基本可用)
b.Soft state(软状态)
c.Eventually consistent(最终一致性)
BASE理论是对CAP中的一致性和可用性进行一个权衡的结果,理论的核心思想就是:我们无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。
3.柔性事务和刚性事务
①:刚性事务:严格遵循ACID原则的事务, 例如单机环境下的数据库事务。
②:柔性事务:指遵循BASE理论(基本可用,最终一致)的事务, 通常用在分布式环境中。(因为分布式事务的特点,只能采用柔性事务)
③:常用的柔性事务解决方案有:(4种)
第一:两阶段提交(Two Phase Commit, 2PC)
有成熟的解决框架,实现简单。
资源锁定周期长,执行效率低,不适合高并发场景
故障恢复困难
第二:TCC补偿型事务(Try - Confirm - Cancel)
实现很复杂,开发成本高
资源锁定粒度小,执行效率高
强隔离性,严格的数据一致性
事务执行周期短
第三:异步确保,基于可靠MQ服务
实现较为复杂,成本略高
对MQ的可靠性要求高
事务执行周期长
第四:最大努力通知
实现简单,成本低
不保证最终一致
4.柔性事务的实现方案
4.1:两阶段提交(2PC)
4.1.1两阶段提交,基于XA协议,以及JTS协议JTA接口。
XA协议指的是TM(事务管理器)和RM(资源管理器)之间的接口,由Tuxedo提出。XA中大致分为两部分:事务管理器和本地资源管理器。其中本地资源管理器往往由数据库实现,比如Oracle、DB2这些商业数据库都实现了XA接口,而事务管理器作为全局的调度者,负责各个本地资源的提交和回滚。XA实现分布式事务的原理如下:
理解重要:XA协议指的是TM(事务管理器)和RM(资源管理器),相当于RM是各种数据库(Oracle、DB2),TM是java代码中的事务管理器(spring中的transaction)。这种协议将RM把各种跨资源库,看成是一个本地库,然后TM就可以作为原来我们一直使用的事务管理方式来即可。
4.1.2在JavaEE平台下,WebLogic、Webshare等主流商用的应用服务器提供了JTA的实现和支持。而在Tomcat下是没有实现的,但是可以借助第三方的框架Jotm、Automikos等来实现,两者均支持spring事务整合。
缺点:
a.两阶段提交中的第二阶段, 协调者需要等待所有参与者发出yes请求, 或者一个参与者发出no请求后, 才能执行提交或者中断操作. 这会造成长时间同时锁住多个资源, 造成性能瓶颈, 如果参与者有一个耗时长的操作, 性能损耗会更明显.
b.实现复杂, 不利于系统的扩展, 不推荐.
4.2:TCC(Try - Confirm - cancel)–重要
4.2.1TCC是基于业务层面的事务定义。锁粒度完全由业务自己控制。它本质是一种补偿的思路。它把事务运行过程分成 Try、Confirm / Cancel 两个阶段。在每个阶段的逻辑由业务代码控制。这样就事务的锁粒度可以完全自由控制。业务可以在牺牲隔离性的情况下,获取更高的性能。
第一:Try 阶段
Try :尝试执行业务
完成所有业务检查( 一致性 )
预留必须业务资源( 准隔离性 )
第二:Confirm / Cancel 阶段:
Confirm :确认执行业务
真正执行业务
不做任务业务检查
Confirm 操作满足幂等性
Cancel :取消执行业务
释放 Try 阶段预留的业务资源
Cancel 操作满足幂等性
Confirm 与 Cancel 互斥
4.2.2实例
在电商网站中,一个经典的案例是这样的:用户下单,基于订单系统实现,操作订单表;同时下单需要对商品进行减库存操作,在库存系统完成;同时还要对用户进行积分奖励,在用户中心完成。这样就出现了分布式事务。
我们用这个业务场景来解释TCC的过程:
try:尝试执行业务
完成业务检查、预留必须业务资源
在本例中:判断库存是否充足,如果充足,锁定库存数据。
confirm/cancel:
Confirm :
确认执行业务,利用try阶段预留的资源进行操作,如果失败还要重试,因此要
保证接口的幂等性
在本例中,我们在库存系统减库存,同时在用户中心给用户增加积分。
Cancel :
如果confirm阶段出现异常、超时。则调用cancel取消执行业务,进行事务补偿
(例如逆向操作)。释放try阶段锁定的资源。如果在事务补偿过程中出现异
常,也必须进行重试。如果超过重试次数后,依然无法成功,则记录日志,以
便后续人工介入进行事务补偿操作。
在本例中,我们尝试恢复库存。同时取消用户积分。
4.2.3优缺点
优点:
①:TCC的try、confirm等操作全部有用户定义,因此锁定粒度由用户自由控制。各个资源独立锁定,分别提交、释放,无需等待对方。失败后是执行cancel中的补偿型操作即可。
②:事务执行效率高,时间短
③:能够保证严格的数据一致性。
缺点:
①:正是因为需要用户编写所有的try、confirm、cancel操作,另外还有重试、幂等的要求,日志的记录等,实现复杂度较高,开发成本增加。
4.2.4目前有一些开源的TCC框架:
TCC-transaction: https://github.com/changmingxie/tcc-transaction(dubbo)
spring-cloud-rest-tcc: https://github.com/prontera/spring-cloud-rest-tcc(springCloud)
happylifeplat-transaction: https://github.com/yu199195/happylifeplat-transaction(上面俩个框架都可以)
4.2.5使用场景
对一致性要求较高,事务时效性比较敏感的业务。(如电商下订单,减库存,加积分)
4.3异步确保
4.3.1概念:
这种实现方式的思路,其实是源于ebay,后来通过支付宝等公司的布道,在业内广泛使用。其基本的设计思想是将远程分布式事务拆分成一系列的本地事务。如果不考虑性能及设计优雅,借助关系型数据库中的表即可实现。
4.3.2原理图:
在这个实现中,将分布事事务,拆分成了主动方事务和被动方事务,通过mq进行异步通知来确保最终的数据一致性。
4.3.3优缺点:
优点:与tcc相比,实现方式较为简单,开发成本低。
缺点:
①:数据一致性完全依赖于消息服务,因此消息服务必须是可靠的。
②:需要处理被动业务方的幂等问题
③:被动业务失败不会导致主动业务的回滚
④:业务与消息服务耦合
4.3.4业务与消息的解耦
刚才的实现中,业务方在进行业务处理的同时,还需要对消息数据进行持久化,代码耦合,不方便以后的维护。
4.3.5优缺点:
优点:
①:消息系统与业务系统解耦
②:消息系统可以独立存储、独立伸缩
缺点:
①:每次消息发送,需要两次请求
②:业务处理系统,需要提供业务状态查询接口
4.3.6使用场景
对事务一致性的时效要求不高的业务。例如跨行转账,支付宝余额宝转账。
4.4最大努力通知
4.4.1原理:
最大努力通知主要用于对于事务的一致性不是特别敏感的事务,实现事务的弱一致性。可以通过消息中间件实现。与前面异步确保型操作不同的一点是, 在消息由MQ Server投递到消费者之后, 允许在达到最大重试次数之后正常结束事务.
①:主业务完成后,通过mq通知被动业务(允许消息丢失)
②:通知失败后主动方 按照一定的时间阶梯进行消息发送重试,直到超过最大重试次数为止
③:主动方提供业务查询接口,以便被动方后续进行通知失败后的补偿,恢复丢失消息。
总结:
1.经典事务
2.分布式事务(柔性事务)
①:俩阶段提交(2pc)
②:TCC(Try - Confirm - cancel)–有框架支持(重要)
③:异步确保(将远程分布式事务拆分成一系列的本地事务,通过消息中间件)–思想
④:最大努力通知(实现事务的弱一致性。可以通过消息中间件实现)–思想
3.项目中一般使用后面3种的方法