25 张图,1.4 w字!彻底搞懂分布式事务原理(二)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 本文提纲如下: 0. 前言 1. 单数据源事务 & 多数据源事务 2. 常见分布式事务解决方案 2.1. 分布式事务模型 2.2. 二将军问题和幂等性 2.3. 两阶段提交(2PC) & 三阶段提交(3PC)方案 2.4. TCC 方案 2.5. 事务状态表方案 2.6. 基于消息中间件的最终一致性事务方案 3. Seata in AT mode 的实现 3.1. Seata in AT mode 工作流程概述 3.2. Seata in AT mode 工作流程详述 4. 结束语

2.6. 基于消息中间件的最终一致性事务方案

无论是 2PC & 3PC 还是 TCC、事务状态表,基本都遵守 XA 协议的思想,即这些方案本质上都是事务协调者协调各个事务参与者的本地事务的进度,使所有本地事务共同提交或回滚,最终达成一种全局的 ACID 特性。在协调的过程中,协调者需要收集各个本地事务的当前状态,并根据这些状态发出下一阶段的操作指令。

但是这些全局事务方案由于操作繁琐、时间跨度大,或者在全局事务期间会排他地锁住相关资源,使得整个分布式系统的全局事务的并发度不会太高。这很难满足电商等高并发场景对事务吞吐量的要求,因此互联网服务提供商探索出了很多与 XA 协议背道而驰的分布式事务解决方案。其中利用消息中间件实现的最终一致性全局事务就是一个经典方案。8.jpg

为了表现出这种方案的精髓,我将使用如下的电商系统微服务结构来进行描述:

9.jpg

在这个模型中,用户不再是请求整合后的 shopping-service 进行下单,而是直接请求 order-service 下单,order-service 一方面添加订单记录,另一方面会调用 repo-service 扣减库存。

这种基于消息中间件的最终一致性事务方案常常被误解成如下的实现方式:

10.jpg

这种实现方式的流程是:

1)order-service 负责向 MQ server 发送扣减库存消息(repo_deduction_msg);repo-service 订阅 MQ server 中的扣减库存消息,负责消费消息。

2)用户下单后,order-service 先执行插入订单记录的查询语句,后将 repo_deduction_msg 发到消息中间件中,这两个过程放在一个本地事务中进行,一旦“执行插入订单记录的查询语句”失败,导致事务回滚,“将 repo_deduction_msg 发到消息中间件中”就不会发生;同样,一旦“将 repo_deduction_msg 发到消息中间件中”失败,抛出异常,也会导致“执行插入订单记录的查询语句”操作回滚,最终什么也没有发生。

11.jpg

3)repo-service 接收到 repo_deduction_msg 之后,先执行库存扣减查询语句,后向 MQ sever 反馈消息消费完成 ACK,这两个过程放在一个本地事务中进行,一旦“执行库存扣减查询语句”失败,导致事务回滚,“向 MQ sever 反馈消息消费完成 ACK”就不会发生,MQ server 在 Confirm 机制的驱动下会继续向 repo-service 推送该消息,直到整个事务成功提交;同样,一旦“向 MQ sever 反馈消息消费完成 ACK”失败,抛出异常,也对导致“执行库存扣减查询语句”操作回滚,MQ server 在 Confirm 机制的驱动下会继续向 repo-service 推送该消息,直到整个事务成功提交。

12.jpg

这种做法看似很可靠。但没有考虑到网络二将军问题的存在,有如下的缺陷:

1)存在网络的 2 将军问题,上面第 2)步中 order-service 发送 repo_deduction_msg 消息失败,对于发送方 order-service 来说,可能是消息中间件没有收到消息;也可能是中间件收到了消息,但向发送方 order-service 响应的 ACK 由于网络故障没有被 order-service 收到。因此 order-service 贸然进行事务回滚,撤销“执行插入订单记录的查询语句”,是不对的,因为 repo-service 那边可能已经接收到 repo_deduction_msg 并成功进行了库存扣减,这样 order-service 和 repo-service 两方就产生了数据不一致问题。

2)repo-service 和 order-service 把网络调用(与 MQ server 通信)放在本地数据库事务里,可能会因为网络延迟产生数据库长事务,影响数据库本地事务的并发度。

13.jpg015

以上是被误解的实现方式,下面给出正确的实现方式,如下所示:

16.jpg

上图所示的方案,利用消息中间件如 rabbitMQ 来实现分布式下单及库存扣减过程的最终一致性。对这幅图做以下说明:

1)order-service 中,

在 t_order 表添加订单记录 &&
在 t_local_msg 添加对应的扣减库存消息

这两个过程要在一个事务中完成,保证过程的原子性。同样,repo-service 中,

检查本次扣库存操作是否已经执行过 &&
执行扣减库存如果本次扣减操作没有执行过 &&
写判重表 &&
向 MQ sever 反馈消息消费完成 ACK

这四个过程也要在一个事务中完成,保证过程的原子性。

2)order-service 中有一个后台程序,源源不断地把消息表中的消息传送给消息中间件,成功后则删除消息表中对应的消息。如果失败了,也会不断尝试重传。由于存在网络 2 将军问题,即当 order-service 发送给消息中间件的消息网络超时时,这时候消息中间件可能收到了消息但响应 ACK 失败,也可能没收到,order-service 会再次发送该消息,直至消息中间件响应 ACK 成功,这样可能发生消息的重复发送,不过没关系,只要保证消息不丢失,不乱序就行,后面 repo-service 会做去重处理。

3)消息中间件向 repo-service 推送 repo_deduction_msg,repo-service 成功处理完成后会向中间件响应 ACK,消息中间件收到这个 ACK 才认为 repo-service 成功处理了这条消息,否则会重复推送该消息。但是有这样的情形:repo-service 成功处理了消息,向中间件发送的 ACK 在网络传输中由于网络故障丢失了,导致中间件没有收到 ACK 重新推送了该消息。这也要靠 repo-service 的消息去重特性来避免消息重复消费。

4)在 2)和 3)中提到了两种导致 repo-service 重复收到消息的原因,一是生产者重复生产,二是中间件重传。为了实现业务的幂等性,repo-service 中维护了一张判重表,这张表中记录了被成功处理的消息的 id。repo-service 每次接收到新的消息都先判断消息是否被成功处理过,若是的话不再重复处理。

17.jpg

通过这种设计,实现了消息在发送方不丢失,消息在接收方不被重复消费,联合起来就是消息不漏不重,严格实现了 order-service 和 repo-service 的两个数据库中数据的最终一致性。

基于消息中间件的最终一致性全局事务方案是互联网公司在高并发场景中探索出的一种创新型应用模式,利用 MQ 实现微服务之间的异步调用、解耦合和流量削峰,支持全局事务的高并发,并保证分布式数据记录的最终一致性。

18.jpg

3. Seata in AT mode 的实现

第 2 章给出了实现实现分布式事务的集中常见的理论模型。本章给出业界开源分布式事务框架 Seata 的实现。

Seata 为用户提供了 AT、TCC、SAGA 和 XA 事务模式。其中 AT 模式是 Seata 主推的事务模式,因此本章分析 Seata in AT mode 的实现。使用 AT 有一个前提,那就是微服务使用的数据库必须是支持事务的关系型数据库。

3.1. Seata in AT mode 工作流程概述

Seata 的 AT 模式建立在关系型数据库的本地事务特性的基础之上,通过数据源代理类拦截并解析数据库执行的 SQL,记录自定义的回滚日志,如需回滚,则重放这些自定义的回滚日志即可。AT 模式虽然是根据 XA 事务模型(2PC)演进而来的,但是 AT 打破了 XA 协议的阻塞性制约,在一致性和性能上取得了平衡。

AT 模式是基于 XA 事务模型演进而来的,它的整体机制也是一个改进版本的两阶段提交协议。AT 模式的两个基本阶段是:

1)第一阶段:首先获取本地锁,执行本地事务,业务数据操作和记录回滚日志在同一个本地事务中提交,最后释放本地锁;

2)第二阶段:如需全局提交,异步删除回滚日志即可,这个过程很快就能完成。如需要回滚,则通过第一阶段的回滚日志进行反向补偿。

本章描述 Seata in AT mode 的工作原理使用的电商微服务模型如下图所示:

19.jpg

在上图中,协调者 shopping-service 先调用参与者 repo-service 扣减库存,后调用参与者 order-service 生成订单。这个业务流使用 Seata in XA mode 后的全局事务流程如下图所示:

20.jpg

上图描述的全局事务执行流程为:

1)shopping-service 向 Seata 注册全局事务,并产生一个全局事务标识 XID

2)将 repo-service.repo_db、order-service.order_db 的本地事务执行到待提交阶段,事务内容包含对 repo-service.repo_db、order-service.order_db 进行的查询操作以及写每个库的 undo_log 记录

3)repo-service.repo_db、order-service.order_db 向 Seata 注册分支事务,并将其纳入该 XID 对应的全局事务范围

4)提交 repo-service.repo_db、order-service.order_db 的本地事务

5)repo-service.repo_db、order-service.order_db 向 Seata 汇报分支事务的提交状态

6)Seata 汇总所有的 DB 的分支事务的提交状态,决定全局事务是该提交还是回滚

7)Seata 通知 repo-service.repo_db、order-service.order_db 提交/回滚本地事务,若需要回滚,采取的是补偿式方法

其中 1)2)3)4)5)属于第一阶段,6)7)属于第二阶段。

相关实践学习
深入解析Docker容器化技术
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。Docker是世界领先的软件容器平台。开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题。运维人员利用Docker可以在隔离容器中并行运行和管理应用,获得更好的计算密度。企业利用Docker可以构建敏捷的软件交付管道,以更快的速度、更高的安全性和可靠的信誉为Linux和Windows Server应用发布新功能。 在本套课程中,我们将全面的讲解Docker技术栈,从环境安装到容器、镜像操作以及生产环境如何部署开发的微服务应用。本课程由黑马程序员提供。     相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
相关文章
|
设计模式 安全 Java
【分布式技术专题】「Tomcat技术专题」 探索Tomcat技术架构设计模式的奥秘(Server和Service组件原理分析)
【分布式技术专题】「Tomcat技术专题」 探索Tomcat技术架构设计模式的奥秘(Server和Service组件原理分析)
264 0
|
存储 分布式计算 Hadoop
Hadoop【基础知识 01】【分布式文件系统HDFS设计原理+特点+存储原理】(部分图片来源于网络)
【4月更文挑战第3天】Hadoop【基础知识 01】【分布式文件系统HDFS设计原理+特点+存储原理】(部分图片来源于网络)
530 3
|
5月前
|
NoSQL 算法 安全
分布式锁—1.原理算法和使用建议
本文主要探讨了Redis分布式锁的八大问题,包括非原子操作、忘记释放锁、释放其他线程的锁、加锁失败处理、锁重入问题、锁竞争问题、锁超时失效及主从复制问题,并提供了相应的优化措施。接着分析了Redis的RedLock算法,讨论其优缺点以及分布式专家Martin对其的质疑。此外,文章对比了基于Redis和Zookeeper(zk)的分布式锁实现原理,包括获取与释放锁的具体流程。最后总结了两种分布式锁的适用场景及使用建议,指出Redis分布式锁虽有性能优势但模型不够健壮,而zk分布式锁更稳定但部署成本较高。实际应用中需根据业务需求权衡选择。
|
8月前
|
运维 NoSQL 算法
【📕分布式锁通关指南 04】redis分布式锁的细节问题以及RedLock算法原理
本文深入探讨了基于Redis实现分布式锁时遇到的细节问题及解决方案。首先,针对锁续期问题,提出了通过独立服务、获取锁进程自己续期和异步线程三种方式,并详细介绍了如何利用Lua脚本和守护线程实现自动续期。接着,解决了锁阻塞问题,引入了带超时时间的`tryLock`机制,确保在高并发场景下不会无限等待锁。最后,作为知识扩展,讲解了RedLock算法原理及其在实际业务中的局限性。文章强调,在并发量不高的场景中手写分布式锁可行,但推荐使用更成熟的Redisson框架来实现分布式锁,以保证系统的稳定性和可靠性。
363 0
【📕分布式锁通关指南 04】redis分布式锁的细节问题以及RedLock算法原理
|
11月前
|
存储 Dubbo Java
分布式 RPC 底层原理详解,看这篇就够了!
本文详解分布式RPC的底层原理与系统设计,大厂面试高频,建议收藏。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
分布式 RPC 底层原理详解,看这篇就够了!
|
存储 分布式计算 监控
Hadoop【基础知识 01+02】【分布式文件系统HDFS设计原理+特点+存储原理】(部分图片来源于网络)【分布式计算框架MapReduce核心概念+编程模型+combiner&partitioner+词频统计案例解析与进阶+作业的生命周期】(图片来源于网络)
【4月更文挑战第3天】【分布式文件系统HDFS设计原理+特点+存储原理】(部分图片来源于网络)【分布式计算框架MapReduce核心概念+编程模型+combiner&partitioner+词频统计案例解析与进阶+作业的生命周期】(图片来源于网络)
569 2
|
10月前
|
机器学习/深度学习 存储 运维
分布式机器学习系统:设计原理、优化策略与实践经验
本文详细探讨了分布式机器学习系统的发展现状与挑战,重点分析了数据并行、模型并行等核心训练范式,以及参数服务器、优化器等关键组件的设计与实现。文章还深入讨论了混合精度训练、梯度累积、ZeRO优化器等高级特性,旨在提供一套全面的技术解决方案,以应对超大规模模型训练中的计算、存储及通信挑战。
538 4
|
12月前
|
分布式计算 Hadoop 网络安全
Hadoop-08-HDFS集群 基础知识 命令行上机实操 hadoop fs 分布式文件系统 读写原理 读流程与写流程 基本语法上传下载拷贝移动文件
Hadoop-08-HDFS集群 基础知识 命令行上机实操 hadoop fs 分布式文件系统 读写原理 读流程与写流程 基本语法上传下载拷贝移动文件
150 1
|
12月前
|
存储 机器学习/深度学习 缓存
Hadoop-07-HDFS集群 基础知识 分布式文件系统 读写原理 读流程与写流程 基本语法上传下载拷贝移动文件
Hadoop-07-HDFS集群 基础知识 分布式文件系统 读写原理 读流程与写流程 基本语法上传下载拷贝移动文件
229 1
|
12月前
|
存储 缓存 数据处理
深度解析:Hologres分布式存储引擎设计原理及其优化策略
【10月更文挑战第9天】在大数据时代,数据的规模和复杂性不断增加,这对数据库系统提出了更高的要求。传统的单机数据库难以应对海量数据处理的需求,而分布式数据库通过水平扩展提供了更好的解决方案。阿里云推出的Hologres是一个实时交互式分析服务,它结合了OLAP(在线分析处理)与OLTP(在线事务处理)的优势,能够在大规模数据集上提供低延迟的数据查询能力。本文将深入探讨Hologres分布式存储引擎的设计原理,并介绍一些关键的优化策略。
575 0

热门文章

最新文章