只要牵涉到分布式系统,无论如何都会碰见分布式事务,当然你可以合理的拆分系统,规划表和库的结构,但是这只是减少分布式事务出现的次数,比方说你原来系统中有5处地方会有分布式事务,现在一优化可能只有3处地方有了,但是你要一点也没有,个人认为不大可能
接下来谈谈什么情况下会产生分布式事务?
一: 同数据库,不同web容器
由上图可知,我们的订单系统和库存系统连着相同的数据源,该数据源里面也只有一个database,里面存放着订单表和库存表。因为订单系统和库存系统是两个不同的系统,部署在了两台服务器上面,他们之间通过接口进行调用,所以虽然它们两张表在同一个数据库里面,但是我们无法进行事务操控。从业务上来讲,当我们下单的时候,商品的库存一定要扣除,不能库存扣了,订单没生成,或者说订单生成了,库存没扣,这样不就乱套了么?所以扣库存和生成订单这两步操作,必须要么全做,要么全不做,这就牵涉到了分布式事务。
二 :不同数据源,相同web容器
这次我们把下单操作和扣库存的操作写在了同一个系统里面并且部署在了一台服务器上面,这个系统连接着两个数据源,分别是订单数据库和库存数据库。显然,虽说我们吧业务逻辑写在一个系统里面了,但是你连着两个数据源,所以又出现了分布式事务。
三不同数据库,不同web容器
第三种情况有点类似于,第一和第二种的结合。每个系统操纵自己的数据库,系统之间通过接口调用,很明显必然又要出现分布式事务
四 单表分片的情况
有的时候一张表的数量实在是太大了,而且增速过猛,那么无可奈何,只能把这张表拆分成好几份存放到不同的数据库服务器上面,有人会说这为什么也会产生分布式事务呢?根据上面那张图我举个例子,假如库存表分片1存的是上海仓库的商品库存信息,库存表分片2存的是北京仓库的商品库存信息,现在我要进行调货操作,从上海仓库调走500台iPhone手机到北京仓库,这个时候我们就要对分片1进行扣减500的操作,而对分片2进行增加500的操作,那么我们这个操作可以允许失败么?我分片1(上海仓库)的500台扣减掉了,但是分片2(北京仓库)没有增加成功?这显然是不可以的,所以这牵涉到了分布式事务,当然我这里只是举一个例子,因为你牵涉到了对同一张表的两条以上的记录进行修改操作,所以引出了分布式事务。
上面我们聊了什么样的情况下会出现分布式事务,接下来说下解决办法
一:jta(二阶段提交)
实际上jta严格来说不能解决分布式事务,比方说我上面举的第一种情况,我是单数据源,不同web容器,你用jta怎么解决?jta只能解决多数据源下的事务,而且对系统的吞吐量影响极大。
二:补偿法
这个是目前最常用的方法
首先我们需要把我们的业务划分到最细,让每个业务成为原子业务,比方说我们有一个业务现在可以划分成3个原子业务,业务A,业务B,业务C。这些业务操作可以是你直接操作数据库,也可以是调用别的系统的接口
假设我们现在业务A业务B都执行成功,但是业务C这里失败了。那么我们有两条路可以走。
第一条:把业务A和业务B回滚,也就是撤销业务A和业务B。
第二条:继续执行业务C直到成功为止
这两种方向我们也叫做正推和逆推,实际上第二种是我们常用的,如果你逆推的话把前面做的都撤销掉了,我感觉意义不大,当你产生补偿的行为的时候,我们的主流程实际上已经走通了,比方说你下单操作,用户点完下单之后,你告诉它你已经成功下单,好了接下来和用户没关系了,这个就叫主流程结束了。但是你的下单流程里面包含了库存的调配,比方说我在上海下的订单,要去北京库房调货,那么牵涉到分布式事务(可以参照分布式事务的第四种情况),好了现在调北京库房调用失败,那么我开始逆推把订单给删了,你觉得合适么?还是说我继续正推,直到我调用成功为止,用户那边我只要在订单上显示正在配货中就可以了,反正你要把订单都给删了,那会是一个很不好的体验