一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第11天,点击查看活动详情。 导致产生事务的根源就是分库分表。
单体的应用
首先看一下单体的应用,在传统的应用当中都是单一的数据库服务,以前做的项目都是基于一个数据库,把所有的业务表都放在一个数据库内。这个时候数据库对这个事务是可以得到很好的支持的。
例如,A 给 B 转账转了 1000 钱,那么这个时候要在 A 的表里边金额减去 1000。在 B 的这个用户的这张表里要加上 1000,它们之间哪一个步骤出错了,事务都会进行回滚,A 和 B 两个用户的账户上的钱是不会变化的。
分布式的应用
在传统的架构设计中,所有的业务表都在一个数据库当中,可以很好的来去支持事物,不会有什么问题,但是随着业务量的增加,我们要对数据库进行拆分。在同一个业务内再把数据进行水平切分。等于是我一个业务里边会有多个数据库。
那么多个数据库之间它的事物是没有办法进行统一管理的。因为你每一个数据库都是一个独立的,它只保存了数据的一部分。这个时候它是没有办法统一管理这个事物的。最后呢就有可能造成数据不一致的情况。
例如,A 给 B 转账转 1000 钱,A 和 B 应该是在一个业务数据库内,因为它两个都是账户,可能是在同一个业务数据库内,但是可能这个同一个业务数据库做了水平的切分。水平的切分有可能 A 用户放在了分片库 1 当中,B 用户分到了分片库 2 当中。这个时候 A 给 B 转账转 1000 钱,这个操作首先要在 A 数据库上减 1000 钱,也就是分片库 1 里边的这张表要减 1000,然后在分别库2 这个 B 的用户当中,要加上 1000。这个时候如果给 B 加 1000 块钱的这个操作出现了什么问题。
那么事务回滚分,片库 2 是可以进行事务回滚,那么分别库 1 怎么办?这两个数据库没有办法做成统一,这个就是分布式事物的这个问题。
这个就是按照垂直切分以后一个简单的架构示意图,有订单库,有用户库,有商品库。前面是应用的集群,分别调不同的数据库。
除了之前转账的例子,还有一个例子。比如现在是一个下单的操作,用户使用积分去购买商品。用户库扣减积分,订单库要生成这个订单,商品库要去扣减库存。等于是我一个下单的操作,操作了三个数据库,那三个数据库同样他们的事物啊是不能够得到统一的保证的。
一个下单的操作,首先要查看用户的积分。因为用户是通过积分去进行支付的。那他支付完成以后,用户的积分要进行扣减,是不是要操作用户的数据库。然后,还要在订单的数据库当中生成一个订单。同样要在商品的数据库,要把这个商品的库存给它扣减。
一个下单的操作操作三个不同的数据库,你哪一个数据库出现了问题,是不是其他的数据库也需要进行回滚。比如扣减库存的时候,发现库存不足了,就要回滚商品扣减库存的这一步就已经回滚了,然后订单之前已经生成好了,也要进行回滚。那么这个时候你商品库的这个事务是不能管理订单的,同样也不能管理用户,这个就是分布式事务的问题。
解决事务问题
解决分布式事务问题的方案:
-
使用基于 XA 协议的两阶段提交。
-
提供事务的补偿机制。
-
基于本地消息表+定时任务的最终一致方案。
-
基于 MQ 的最终一致方案。