认知故事
从传统的软件系统到如今烂大街的分布式系统,我从事这个行业已经7年,遇到大大小小的问题不计其数。但是有2类问题是我目前的经历中最难处理的。一类是随着互联网的兴起,数据也随着爆炸增长,大数据量的处理,比如高并发请求、大数据量存储等,另一类就是分布式系统带来的分布式事务问题,今天我们来聊一聊分布式事务问题。
数据一致性的定义
我们先来看看数据一致性的定义,这个定义我也看到或者听到过很多,就我自己归纳来说我觉得任何人在任何时间、任何地点以任何接入方式访问任何服务数据都是一致的就是数据一致性最精准的的定义。你可以去看各种所谓的大牛写的书、博客等,但是你必须有自己的理解。因为别人写的书、博客那是别人的思维,不一定匹配你的思维,就像小时候读书的时候总是看不懂课本上的东西,其实不是你的错,因为可能写课本的人思维不匹配你的思维,所以你无法理解。
数据不一致性产生的原因
数据不一致性产生的原因,聊这个之前我们要提到一个概念,那就是本地事务。事务我相信大家都明白这里就不多解释了,不清楚的人可以去查查资料。那么当数据分散在多处,即分散在多个数据库、缓存,当你的本地事务不能保证一次业务流程(请求)的原子性、一致性、隔离性、持久性的时候就产生了数据不一致。

如上图,当软件系统从单体架构到面向服务架构再衍生到微服务架构,我们的本地事务也就衍生成了分布式事务。说的通俗易懂一点,在单体架构中我们可以通过本地事务保证我的一次业务流程(请求)操作用户表、商品表、交易表数据一致性。但是在面向服务架构或者微服务架构体系中我们的本地事务就做不到这点,因为我们的用户表或者缓存、商品表或者缓存、交易表或缓存是在不同的数据库或缓存服务器中。
分布式事务场景
常见的电商平台购买商品下单到减库存到支付这种分布式业务(请求)可以分为两种驱动方式:
同步
异步
同步指我们的一次业务流程(请求)如果要操作用户服务、商品服务、交易服务一般我们是通过另外主调服务的业务逻辑层来驱动的。异步指我们的一次业务流程(请求)如果要操作用户服务、商品服务、交易服务我们一般是通过消息中间件来驱动的。两种方式的示意图如下:


现在来思考一下我们举例的这个分布式业务流程的两种驱动方式场景下怎么去解决分布式事务问题。首先我写一段伪代码说明一下:
begincall orderService;insert into order(...);call goodsService;update goodsStorage(...);call payServie;insert into pay(...);commit;或者beginmsg.send(下单);msg.send(减库存消息);msg.send(支付消息);commit;
以上这段伪代码能解决分布式事务问题吗?或者说你有这么干过么?我想说的是异步这种方式我干过,曾经年少无知的时候天真的认为:
下单消息
减库存消息
支付消息
放进一个本地事务
就能解决异步驱动场景情况下的分布式事务问题。但是不管技术怎么发展,我们一定要保持清醒的头脑,透过现象看到本质的问题。关系型数据库事务中不是一个表就不是一个原子操作,不管你的写法多么花里胡哨都不可能保证数据的一致性的。
上面我们聊了数据一致性的定义、数据不一致性的原因以及同步和异步两种驱动场景下的分布式事务问题。接下来我们来看看分布式事务以及解决方案。
分布式事务的分类
我查了很多资料分布式事务可以分为刚性分布式事务和柔性分布式事务。具体分类如下:
刚性分布式事务
强一致性
XA模型
CAP
CP
柔性分布式事务
最终一致性
CAP、BASE理论
AP
刚性分布式事务中的强一致性就是我们说的本地事务,XA模型相信大家都听说过,后面我们详细分析,至于刚性事务中的CAP肯定就是CP了。而柔性事务即我们为了提高系统的并发能力我们选择最终一致性,CAP我们就自然而然的选择AP了。
刚性分布式事务
强一致性
强一致性就是指我们要满足传统事务的特性,即ACID(Atomicity-原子性、Consistency-一致性、Isolation-隔离性、Durability-持久性)。
XA模型
XA是X/Open CAE Specification(Distributed Transaction Processing)模型中定义,XA规范由AP、RM、TM组成。其中应用程序(Application Program,简称AP):AP定义了事务边界(定义事务开始和结束)并访问事务边界内的资源。资源管理器(Resource Manager,简称RM):RM管理计算机共享的资源,资源即数据库等。事务管理器(Transaction Manager,简称TM):负责管理全局事务,分配事务唯一标识,监控事务的执行进度,并负责事务的提交、回滚、失败恢复等。示意图如下:

XA模型业界内实践
在业界内基于XA模型的实践相信大家也听说过,就是我们常说的2阶段或者3阶段提交。其过程基本如下:
AP发起事务commit请求
TM发起prepare投票
RM都同意后,TM再发起commit
commit过程出现宕机等异常,节点服务重启后,根据XA recover再次进行commit补偿
2阶段或者3阶段处理方案的系统时序图如下:

但是2阶段或者3阶段提交处理方案缺点也很明显:
同步阻塞模型
数据库资源锁定时间过长
全局锁(隔离级别串行化),并发低
不适合长事务场景
举个常见的例子,比如现在很常见的拼团活动,首先是组织者发起拼团通知,交纳拼团费用,必须是等到成团人数满足后后此次活动才能成团(数据强一致性),如果人数满足后就成团了,如果人数到deadline date还没达到要求就取消此次拼团然后退回已经缴纳的拼团费用。
柔性分布式事务
柔性分布式事务是XA协议的妥协,它通过降低强一致性要求,从而降低数据库资源锁定的时间,提升可用性。柔性分布式事务中的CAP的选择是CA,因为分布式环境下网络分区是一定需要的。柔性分布式事务也是对BASE理论一种实践,即Basically Available-基本可用,Soft state-柔性状态,Eventual consistency-最终一致性。而柔性事务中典型的架构实现有以下两种:
TCC模型
Saga模型
TCC模型
所谓TCC模型就是Try-Confirm-Cancel的分布式事务处理模型。TCC模型完全交由业务实现,每个子业务都要实现Try-Confirm-Cancel三个接口,对业务侵入极大,并且资源锁定也交由业务方。Try步骤尝试执行业务,完成所有业务检查,预留必要的资源。Confirm真正执行业务,不再做业务检查。而Cancel就是分布式事务协商失败时释放Try阶段预留业务资源。举个具体的例子,比如我们常见的汇款服务和收款服务,小明通过某支付平台向小龙汇款1000元,如果我们要用TCC处理分布式事务,那么我们一步操作就要变成三步操作。具体如下:
* 汇款服务* Try* 检查小明账户有效性,即查看小明账户的状态是否为“转账中”或者“冻结”* 检查小明账户余额是否充足* 从小明账户中扣减1000元,并将状态更新为“转账中”* 预留扣减资源,将从小明账户往小龙转账1000元这个事件存入消息或日志中* Confirm* 不做任何操作* Cancel* 小明账户增加1000元* 从消息或者日志中获取预留的资源并释放* 收款服务* Try* 检查小龙账户是否有效* Confirm* 读取消息或者日志,小龙账户增加1000元* 从消息或者日志中获取预留的资源并释放* Cancel* 不做任何操作
其实TCC模型就是XA模型的一种妥协实现。其缺点很明显,业务侵入比较大,即每个业务接口都要实现Try、Confirm、Cancel业务操作,而且业界内没有比较好的公司或者组织开源实现,基本上都个人实现开源。所以建议大家了解即可,不做推荐。
Saga模型
Saga模型起源于1987年Hector Garcia-Molina,Kenneth Salem发表的论文《Sagas》。Saga模型把一个分布式事务拆分为多个本地事务,每个本地事务都有相应的执行模块和补偿模块(对应TCC中的Confirm和Cancel),当Saga事务中任意一个本地事务出错时,可以通过调用相关的补偿方法恢复之前的事务,达到事务最终一致性。当每个Saga子事务T1,T2,...,Tn都有对应的补偿定义C1,C2,...,Cn-1,那么Saga系统可以保证:
子事务序列T1,T2,...Tn得以完成(最佳情况)
或者序列T1,T2,...Tj,Cj-1,...C2,C1,0 < j < n 得以完成
Saga的隔离性通过在业务层控制并发,在应用层加锁,预留冻结资源实现。Saga模型的恢复方式分为向后恢复和向前恢复。向后恢复即补偿所有已完成的事务,如果任一子事务失败。向前恢复即重试失败的事务,假设每个子事务都最终会成功。

分布式事务处理的哲学问题
以上我们讨论了关于分布式事务一系列问题包括分布式事务的由来、数据不一致的原因、同步和异步两种驱动场景下分布式事务、分布式事务的分类,最后我们详细分析了业界内两种柔性分布式事务解决方案。此篇分布式事务设计与实践(上)关于理论部分就讨论到这里,下篇我们来详细说说Saga分布式事务的实现方案。但是再这之前我想和大家讨论一个问题,那就是你觉得分布式事务复杂么?至少我认为是比较复杂的。那我们一定要用分布式事务吗?讲个故事给大家,我们都知道圆珠笔书写的过程中会有磨损,当写到一定程度的时候就会开始漏油,所以以前有一家公司投入了资源去解决圆珠笔漏油的事情,但是效果都不好,后来有一个人就想我能不能规避这个问题呢?比如一般圆珠笔写到2W字的时候就开始漏油了,那我在设计圆珠笔的时候能不能就让笔墨只能写19000字呢?所以,我们一定要用分布式事务吗?




