00:00
那接下来我们就来讨论一下分布式事务,分布式事务相关的资料,我们可以来参加我们第三章本地事务与分布式事务。好,我们来打开这个资料,我们再说分布式事务之前先来回顾一下我们一直用的本地事物,那什么是本地事务,那就是我们现在呢,有一个单体应用,它呢就连了一个数据库也没有,多个数据库,也没有多个项目,也不牵扯到远程调用,们现在要做一系列的事情,我们下订单,下完以后减库存,减完以后再减积分,那么这三个操作要么同时成功,要么同时失败,这就是我们在单体应用时代,我们经常会用到本地事物,那本地事物的几个特性,事物的几个特性我们在讲数据库的时候呢,就已经说了很多遍了,我们的acida代表原子性。它指的就是我们一系列的操作,我们的下订单,减库存,减积分,这是一个完整的业务逻辑操作它呢是一个不可分割的,由这三个呢,要么同时成功,要么同时失败,不可能存在两个成功,一个失败,所以呢,这是说的原则性,还有另外我们说的一致性,那一致性就是指比如以转账为例,那么原来呢,A由1000 B呢有1000,如果我们A给B转200成功了,那A就是800 B呢就是1200,那么业务前后呢,他们两个的总量都是2000,那是一致的,我们转完账以后呢,两个总量也都是2000,不可能出现一个我转完账了,他扣了800,他没加200,这是我们说的业务上的一致性,我们事物前后一定要保证我们业务的一致,另外就是隔离性,我们事物之间互相隔离,比如这个数据库同时呢,可能有多个事物都在执行我们这个下单,我们这一个数据库呢,我们现在100个人同时下单,那一个人下。
01:51
失败了,它的数据回滚不会影响任何其他人,所以呢,事物之间是应该是隔离的,包括我们说的持久性,一旦我们这个事务成功,我们这三个操作呢,成功数据库都告诉你,诶得得得得全成功了,那就一定会成功,我们数据库呢,即使我们返回前一刻,它告诉你成功了,刚告诉完你成功数据库咔断电了,那你再次启动数据库,那这个数据呢,一定是成功在数据库里边的,这就是我们说的持久性,那我们数据库的,我们事物的acid这四个基本特性,我们说了很多遍,总之就是一句话,我们现在呢,想要保证这几个操作它呢是同一个事物,要么同时成功,要么同时失败,本地事物呢,就指的是我们这三个操作连的是同一个数据库,我们使用同一条连接,我们跟数据库建立了一条链接,就在这条链接内,我发了三个circle口,第一个呢改库存,第二个呢改订单,第三个改我们积分。只要有任何一个失败,这三。
02:51
的circle呢,全部回滚,那么这个本地事物呢,也非常简单,我们想要做呢,直接使用spring一个注解transion的,我们就直接做了这个事物,那我们以前呢,也经常来用,我们再来使用transition的时候呢,还有几个比较重要的细节,我们也可以说一下,第一个是我们事物的隔离级别,隔离级别呢,是我们数据库,我们买circleq,我们circleq规定的一些规范隔离级别,比如我们有这几个隔离级别,第一个叫独未提交意思呢,如果我们某一个事物,它的隔离级别是独未提交意思,在我们这个事物里边可以读到别人还没有提交的数据。
03:29
那这样呢,就会出现我们的脏毒现象,别人这个数据呢,没提交被我们读出来了,我们拿在下边用,结果别人给毁混了,那么拿的这个数据呢,就是假的,所以呢,我们说读已提交会有脏毒现象,还有读已提交,那就是我们这个事物可以读到别人已提交的数据,那这样别人已提交的数据,那相当于呢,就是执行成功了的,我们可以在我们的事务里边就可以读到,我们业务里边呢,也经常会用到读已提交,我们成为这个隔离级别,而且呢,我们这个orlook和circle serve数据库默认的隔离级别就是独已提交,那也就是比如我们这个大事物,在我们这个事物,假设我们这个事物是一个本地事物,没有远程调用,那在我们这个事物期间内,我们去来读数据库,只要别人把这个数据提交了,我们都能读的出来,我们如果想要调整事物的隔离级别,我们当前这个事物,我们就可以直接在这儿有一个叫isolation,这一块呢,我们就可以调整隔离级别,隔离级别那就是我们这个。
04:29
属性把它拿过来,如果我们想要调,我们点过来,那它的这个隔离级别呢,就有一个叫read commit,那就是我们这个事务期间内都能读到已提交的数据,那read UN commit,那就是我们能读到未提交的数据,但是如果我们现在是使用的my circle,那它的隔离级别呢,是将read repeatable read叫可重复读,可重复读的意思就是在我们整个事务期间内,只要我们事务没结束。第一次我们去数据库读到的,比如一号记录,它的值是100,我们在整个事误期间,无论读多少次数据库,一号记录它的值还是100,即使呢,外边的人在操作这个数据的时候,把这个数据都删了,或者呢,把它修改成200了,我们读到的都是100,所以我们把这个称为可重复读,我们可以重复读取多次,读取到的值呢,都是一样的,可重复读呢,我们不调整,这也是默认的,这是my circleq默认的隔离级别,我们以前说的隔离级别,我们再来复习一下,还有我们最后说的串行化,序列化,Sirleible,如果数据库隔离级别调成一个序列化的,那每一个事物。
05:39
每做一件事,那都等这个做完了才能做下一个,我们事物呢,整个数据库就没有任何并发能力了,所以我们的这个隔离级别就是从小到大,我们越大的隔离级别,我们并发能力越低,这是我们说的隔离级别,数据库呢,有默认的可重复读,我们想要调整某一个事物也能调整,在这儿呢,直接来调。还有我们说的这个传播行为,这个呢也是我们经常本地事物里边用的,我们给大家来复习一下,咱们讲这个框架的时候呢,我们已经把它详细介绍过了,我们就现在再来串讲一下就行了。那么这个传播行为呢,叫propagation,这个传播行为有很多种,我们非常常用的呢,就是这两种,一个叫require,还有一个叫requires new来说一下这两个传播行为,再来复习一下require和我们的这个requires new传播行为指的就是。
06:33
我们的这个事物假设呢,我们敲了这么一个代码,我现在呢,假设我现在有一个方法public word,我现在有一个A方法,A方法呢,它是一个事物,我们叫transal,然后呢,它调用呢,B方法和C方法public VO b方法,B方法呢也是一个事物方法,然后呢,它还调用了C方法,C方法呢也是一个事物方法,那相当于在A里边调用了B,还调用了C。传播行为指的就是我们BC这两个小事物,要不要跟A共用一个事物,比如我们这一块呢,有一个叫require require的呢,就是我们需要事物,只要谁的这个传播行为,比如B,我来把这个传播行为它呢调成require。
07:25
也就是说我们这个B呢,它需要一个事物,我来点进来,我们来调一下,那默认呢,也就是require,我把这个复制过来,那这样呢,我们这个B方法一定要要一个事物,那这个事物如果我们前边呢,已经有了这个事物propagation,我会发现呢,如果B调成我需要一个事物,前边A这个事物已经有了,那B跟A就共用了一个事物,相当于B跟A就在同一条连接里边执行,而如果C调成了我们的这个request new代表呢,我跟你不共用一个事物,Request new翻译过来就是总是需要一个新的,所以呢,这个C就是一个新事物,那我们这个A呢,假设是A事物,A事物,那如果B是我们说的required required代表我需要事物,如果有就用之前的,之前的是A事物,那么就用之前的,那就是A15 AB呢都在一个事物里边,那这样假设我们出现了一个问题,应它A等于十除零。
08:25
那大家说哪个会回滚呢?我们现在来看,如果I等于十除零,异常一抛,我们相当于A方法就炸了,因为我们这个事物呢,只要有异常,我们就要回滚,那AB既然回滚了,B呢,肯定也跟着回滚,因为AB是同一个事物,但人家C是一个新事物,所以呢C不回滚,也就是说所有的里边都是可以控制的,它可以不回滚,简单来说就是我们BC2个方法要不要做A这辆车,你要做了A的这辆车了。那A炸了,我们跟着炸,包括B炸了,A也得跟着炸,如果我们不做A这辆车,像C一样,A再怎么着我们都不会影响到我们C事物的执行,所以么?这就是我们说的传播行为,特别我们说传播行为既然要共用一个事物了,假设我们在事物里边有一个设置叫timeout事物的超时时间,比如我们设置了30秒,意思我们这个事物只要30秒没返回,我们就给它回滚,我们A设了超时时间30秒,B如果我们再来设了超时时间30秒。
09:30
那大家说我们B呢?假设设置了两秒,那大家说如果B的方法执行了七秒,那我们这个B算事物超时还是不超时呢?我们说传播行为另外的意义就是我们既然B说了,哎,我是required,我需要事物跟A共用一个事物。那相当于A这个事物设置是什么,那B呢,跟着就是什么,B这一块的其他设置就相当于没用了,因为他都跟人家A是共用一个事物的,所以A事物的所有设置就会传播到跟他共用一个事物的人,A事物的所有设置就传播到了和他共用一个事物的方法。
10:14
所以呢,A这一块设置了什么,B这一块呢,就直接继承我们这个设置,B这一块再写什么设置都没用,你想要写有用,你除非是一个新事物,别跟我共用一个事物,那C呢,这个设置就是有用的,这是我们回顾的传播行为,如果这块呢,大家还不了解的同学。可以来参照我们讲的W框架里边的事物复习一下就行了,我们现在来给大家快速的来过一遍,再来说一下我们w boot里的物,因为我们想要控事物最简单的方式就是使用spring boot的事物,它呢就是spring给我们提供的事物注解,Transitional一个注解呢就完事了,只要我们是事务注解标注的下边的所有的数据库的执行有一个失败,全体呢都会失败,我们都会自动回滚,当然这是我们说的本地事物模式下当是在我们这个boot这个事物里边呢,还有一个坑,我们给大家呢也来顺便提一下,这个坑就是如果我们照着我的这个方法这么来写,A掉了B事物和C事物BC大家注意啊,ABC是同一个service里边的这些方法,而且呢,我A调的是BC这两个方法,这样的话呢,BC2个方法,你在这一块来做任何的事物设置。
11:35
都是没用的,如果我们是这种模式下,BC做任何设置,做任何设置都没用,都是和A共用一个事物,都是和A共用一个事物,原因就在于我们说事物呢,最大的特点它是一个代理。如果我们。这个BC方法是其他的service,比如我们有一个b service,我们调了b service的B方法,调了c service的C方法,那这样呢,这个事物事物设置是可以有用的,因为我们这个事物呢,它生成的是代理对象,事物是用代理来做的,代理对象来控制的。
12:21
而如果我们直接去来调BC,我们自个儿的这两个方法,好,我们这个呢是可以的,如果我们直接来调我们BC的自个两个方法,相当于我们跳过了代理,那相当于直接是A方法把BC方法的这段代码复制粘贴过来了,所以呢,本质上来说,你调B方法跟你把B方法的这一段代码粘到这儿没有任何区别,相当于都是跟A共用一个事物,所以这种情况下,我们还想要本方法内我们同一个对象里边方法互调,事物还要生效,我们事物的这些设置要生效。那怎么做,所以呢,这一块的坑就是在这儿,我们同一个对象,同一个对象,那咱们这个方法事物方法互掉,事物方法互掉失效问题,默认呢,失效,失效的原因就是它绕过了原因,那么这样直接一调就是复制粘贴,它是相当于绕过了,绕过了咱们这个代理对象。
13:24
A方法的调用肯定是拿代理对象调的,但是A的BC里边相当于直接把代码复制粘贴过来,如果我们能拿代理对象调,比如我们把自己的service order service,我们再来调order service的B方法,Order service的C方法,那这不就成了吗?但如果我们拿this来调,也是没用的,因为this本身就是当前这个对象是没有经过代理的,我们在这儿this也是没用的。所以我们现在呢,想要调这个,让他能生成事物,我们唯一的做法就是没用。哎,我们来说一下没用。有的同学想,我能不能把这个order service,我现在要调自己,我把自己呢再注入一遍,来注一个order service。
14:07
把自己呢,再注入一遍,我就能注入我自个儿的代理对象,我拿过来,我在这掉,这样肯定是爆炸效果,我们千万不能这样写,相当于order service循环依赖,我依赖我自个儿,然后我要注入我自个儿的时候,我们自个儿呢,依相当于又有一个属性,又是依赖自个儿,我们循环依赖了,所以我们肯定不能这么写,所以我们想要解决我们这个问题呢,Boot给我们提供了一个方案,我们就是这么来做的。好把这个呢,直接写到我们这个主类里边,来给大家回顾一下这一块的解决办法。事物失效问题,本地事务失效,那这个本地事务失效呢,指的是同一个对象内事务方法的互调,它绕过了这个代理对象,那这个事物设置呢,都是没有用的,当然事物是能加上你的事物设置没用,你设置超时多少秒,怎么多少秒,这些都没用,那我们的解决方案就应该是这样子的。
15:08
解决方案就围绕一个主题,就是使用代理对象来调用就行了,使用代理对象来调用事务方法,那怎么解决这个问题呢?我们就可以这么来做,首先第一个引入a up模块up up的starter,你们都知道spring加A这个动态代理,它的这个场景呢,是我们这个AOP start,那现在来引入这个AOP start a op start AOP,我们这是第一步,我们引入这个场景,引入这个场景呢,主要想要用它里边的这个叫PE接,相当于使用它来做一个动态代理,所以我们要做的第一件事引入它,然后呢,他呢帮我们引入了安思de特接,这个动态代理呢更强大,然后接下来第二步。第二步,我们开启我们这个aspect间动态代理功能,叫enable,我们开启aspectct间动态代理,也就是说因为我们不使用这个注解的话,它默认是使用JDK自动生成的,按照接口的动态代理,我们开启我们的安PE接动态代理。
16:17
动态代理功能,这样呢,以后所有的动态代理都是用它创建的,而不是我们JDK默认的动态代理,以后所有的所有的动态代理都是他帮我们创建的,他创建的好处就是我们即使没有接口也能代理,即使以前我们JDK默认的动态代理必须有接口,现在呢,即使没有接口,没有接口。也可以创建动态代理,也。可以创建动态代理。所以呢,我们相当于第二步,我们让它PE接帮我们代理代以后呢,再来设置一个项叫ex expose proxy叫暴露代理对象,你叫true,所以呢,我们在这来设置,加上它就是对外暴露我们这个代理对象,对外。
17:14
对外。暴露代理对象,这是我们做的第二步,第三步,如果我们还想实现之前的功能,那么以后呢,就用代理对象调用,用代理对象本类互调。本类互调呢,都要用代理对象来调就行了,那这个代理对象怎么获取,我们点进来它都提示了,我们想获取代理对象用a op contest,所以我们以后啊,想要这么来做,我们只需要用a op contest的上下文拿到当前代理对象,放心大胆的把它转为接口,或者我们这个实现类没问题,Order service,比如employment,我们把这个转成我们指定的这个类型,我们就叫order service,转好以后呢,我们接下来想要调BC方法了,我来调我的B,再来调我的C,这样呢,就是使用代理对象调的,然后我们的这一块设置才会有用。
18:12
所以呢,核心代码就在这儿,如果我们有这个坑,我们就应该这么来解决,当然我们现在没有牵扯到本类方法调用,没有牵扯本类方法调用的这一块都不用做,我们有本类方法互掉的,我们都这么来做就行了,我们把这一块的示例代码就给大家放在这儿啊,作为一个参考,那至此呢,我们就回顾了一下我们所有的本地事物,那下一课我们再来讨论一下分布式事物。
我来说两句