分布式事务问题解决方案

507 阅读5分钟

1 什么是分布式事务:

我觉得WIKI百科的解释清晰而又直接:

分布式事务是涉及两个或更多网络主机的数据库事务。 通常,主机提供事务资源,而事务管理器负责创建和管理包含针对此类资源的所有操作的全局事务。 与任何其他事务一样,分布式事务必须具有全部四个 ACID(原子性、一致性、隔离性、持久性)属性,其中原子性保证工作单元(操作包)的全有或全无结果。

2 分布式事务产生的原因

2.1 数据库分库

单表数据大于千万级,会使B+树索引高度提升,查询效率随之变慢,需要考虑分表,当数据库并发请求数多到一定程度,单个数据库的线程池无法满足大量的并发需求,则需要一套数据库分库方案,随之带来分布式事务问题。

2.2 应用微服务化

将一个复杂的应用拆分成多个服务模块,每个模块专注于单一业务功能对外提供服务,独立部署,同时各模块之间可以互相通信,组合为整体对外提供服务,服务的独立部署一般伴随着数据库的独立部署,也带来分布式事务问题。

3 如何解决分布式事务(How to deal with distributed transactions):

3.1、强一致性实现方案:

3.1.2、基于XA协议的两阶段提交(2PC)

XA是一个分布式事务协议,由Tuxedo提出。XA规范了分布式事务的事务管理器(Transaction Manager)和资源管理器(Resource Manager)之前的交互的接口。其中本地资源管理器往往由数据库实现,比如Oracle、DB2、Mysql(5.0.5后 InnoDB引擎),且这些商业数据库都实现了XA接口,而事务管理器作为全局的调度者,负责各个本地资源的提交和回滚。XA实现分布式事务的原理如下:

总的来说,XA协议比较简单,而且一旦商业数据库实现了XA协议,使用分布式事务的成本也比较低。但是,XA也有致命的缺点,

1 那就是性能不理想,特别是在交易下单链路,往往并发量很高,XA无法满足高并发场景。

2 XA目前在商业数据库支持的比较理想,在mysql数据库中支持的不太理想,mysql的XA实现,没有记录prepare阶段日志,主备切换回导致主库与备库数据不一致。许多nosql也没有支持XA,这让XA的应用场景变得非常狭隘。

3 XA强依赖事务管理器中心,在业务系统之外,徒增了一个具有较大风险的中间应用。

3.1.2、TCC编程模式

所谓的TCC编程模式,也是两阶段提交的一个变种。

TCC提供了一个编程框架,将整个业务逻辑分为三块:Try、Confirm和Cancel三个操作。以在线下单为例,Try阶段会去扣库存、扣活动券,Confirm阶段则是去插入订单表,如果在Try和过程中失败,则进入Cancel阶段,会去恢复库存和券。实践来看,Confirm一般是同步RPC调用,Cancel阶段一般为异步。总之,TCC就是通过代码人为实现了两阶段提交,不同的业务场景所写的代码都不一样,复杂度也不一样,并不通用。

以上是两种强一致性的分布式事务解决方案。

3.2弱一致性实现方案:

3.2.1 消息事务+最终一致性

所谓的消息事务就是基于消息中间件的两阶段提交,本质上是对消息中间件的一种特殊利用,它是将本地事务和发消息放在了一个分布式事务里,保证要么本地操作成功成功并且对外发消息成功,要么两者都失败,开源的RocketMQ就支持这一特性,具体原理如下:

注释:B系统指代整个下游系统.

1、A系统向消息中间件发送一条预备消息

2、消息中间件保存预备消息并返回成功

3、A执行本地事务

4、A发送提交消息给消息中间件

通过以上4步完成了一个消息事务。

虽然上面的方案能够完成A系统和B的系统操作,但是A和B并不是强一致的,而是最终一致的,我们在这里牺牲了强一致性,换来了性能的大幅度提升。当然,基于事务消息的分布式事务实现方案是有风险的,极端情况消息中间件丢失消息或则下游业务系统B一直不执行成功,那么一致性的诉求就无法得到保证。

4、总结

分布式事务,本质上是对存在多个数据库上的事务进行统一控制。从强弱一致性的维度来划分,分为两阶段提交这(2PC)种强一致性,但性能较弱的方案;也有消息中间件实现的弱一致性,但性能可靠的方案。但技术永远是服务于业务诉求的,技术的最终选型也取决于当前业务更匹配哪种实现方案。