| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 大数据 -> 分布式事务总结(2) -> 正文阅读 |
|
[大数据]分布式事务总结(2) |
随着业务的快速发展、业务复杂度越来越高,几乎每个公司的系统都会从单体走向分布式,特别是转向微服务架构。随之而来就必然遇到分布式事务这个难题,这篇文章总结了分布式事务最经典的解决方案,分享给大家。 我们拿转账作为例子,A 需要转 100 元给 B,那么需要给 A 的余额-100 元,给 B 的余额+100 元,整个转账要保证,A-100 和 B+100 同时成功,或者同时失败。看看在各种场景下,是如何解决这个问题的。 ◆ 事务 事务具有 4 个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为 ACID 特性。 Atomicity(原子性):一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被恢复到事务开始前的状态,就像这个事务从来没有执行过一样。 Consistency(一致性):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。完整性包括外键约束、应用定义的等约束不会被破坏。 Isolation(隔离性):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。 Durability(持久性):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。 ◆ 分布式事务 分布式事务就是指事务的发起者、资源及资源管理器和事务协调者分别位于分布式系统的不同节点之上。在上述转账的业务中,用户 A-100 操作和用户 B+100 操作不是位于同一个节点上。本质上来说,分布式事务就是为了保证在分布式场景下,数据操作的正确执行。 分布式事务在分布式环境下,为了满足可用性、性能与降级服务的需要,降低一致性与隔离性的要求,一方面遵循 BASE 理论(BASE 相关理论,涉及内容非常多,感兴趣的同学,可以参考 BASE 理论): 基本业务可用性(Basic Availability)柔性状态(Soft state)最终一致性(Eventual consistency)同样的,分布式事务也部分遵循 ACID 规范: 原子性:严格遵循一致性:事务完成后的一致性严格遵循;事务中的一致性可适当放宽隔离性:并行事务间不可影响;事务中间结果可见性允许安全放宽持久性:严格遵循 ◆ 分布式事务的解决方案 XA 一共分为两阶段: 第一阶段(prepare):即所有的参与者 RM 准备执行事务并锁住需要的资源。参与者 ready 时,向 TM 报告已准备就绪。第二阶段 (commit/rollback):当事务管理者?确认所有参与者(RM)都 ready 后,向所有参与者发送 commit 命令。目前主流的数据库基本都支持 XA 事务,包括 mysql、oracle、sqlserver、postgre XA 事务由一个或多个资源管理器(RM)、一个事务管理器(TM)和一个应用程序(ApplicationProgram)组成。 把上面的转账作为例子,一个成功完成的 XA 事务时序图如下: XA 事务的特点是: 简单易理解,开发较容易 对资源进行了长时间的锁定,并发度低 如果读者想要进一步研究 XA,go 语言可参考 DTM,java 语言可参考 seata ◆ SAGA 把上面的转账作为例子,一个成功完成的 SAGA 事务时序图如下:
并发度高,不用像 XA 事务那样长期锁定资源 需要定义正常操作以及补偿操作,开发量比 XA 大 一致性较弱,对于转账,可能发生 A 用户已扣款,最后转账又失败的情况 论文里面的 SAGA 内容较多,包括两种恢复策略,包括分支事务并发执行,我们这里的讨论,仅包括最简单的 SAGA SAGA 适用的场景较多,长事务适用,对中间结果不敏感的业务场景适用 如果读者想要进一步研究 SAGA,go 语言可参考 DTM,java 语言可参考 seata ◆ TCC TCC 分为 3 个阶段 Try 阶段:尝试执行,完成所有业务检查(一致性), 预留必须业务资源(准隔离性) Confirm 阶段:确认执行真正执行业务,不作任何业务检查,只使用 Try 阶段预留的业务资源,Confirm 操作要求具备幂等设计,Confirm 失败后需要进行重试。 Cancel 阶段:取消执行,释放 Try 阶段预留的业务资源。Cancel 阶段的异常和 Confirm 阶段异常处理方案基本上一致,要求满足幂等设计。 把上面的转账作为例子,通常会在 Try 里面冻结金额,但不扣款,Confirm 里面扣款,Cancel 里面解冻金额,一个成功完成的 TCC 事务时序图如下: 并发度较高,无长期资源锁定。 开发量较大,需要提供 Try/Confirm/Cancel 接口。 一致性较好,不会发生 SAGA 已扣款最后又转账失败的情况 TCC 适用于订单类业务,对中间状态有约束的业务 如果读者想要进一步研究 TCC,go 语言可参考 DTM,java 语言可参考 seata ◆ 本地消息表 大致流程如下: 容错机制: 扣减余额事务 失败时,事务直接回滚,无后续步骤 轮序生产消息失败, 增加余额事务失败都会进行重试 本地消息表的特点: 长事务仅需要分拆成多个任务,使用简单 生产者需要额外的创建消息表 每个本地消息表都需要进行轮询 消费者的逻辑如果无法通过重试成功,那么还需要更多的机制,来回滚操作 适用于可异步执行的业务,且后续操作无需回滚的业务 ◆ 事务消息 事务消息发送及提交: 发送消息(half 消息) 服务端存储消息,并响应消息的写入结果 根据发送结果执行本地事务(如果写入失败,此时 half 消息对业务不可见,本地逻辑不执行) 根据本地事务状态执行 Commit 或者 Rollback(Commit 操作发布消息,消息对消费者可见) 正常发送的流程图如下: 对没有 Commit/Rollback 的事务消息(pending 状态的消息),从服务端发起一次“回查”Producer 收到回查消息,返回消息对应的本地事务的状态,为 Commit 或者 Rollback 事务消息方案与本地消息表机制非常类似,区别主要在于原先相关的本地表操作替换成了一个反查接口 事务消息特点如下: 长事务仅需要分拆成多个任务,并提供一个反查接口,使用简单 消费者的逻辑如果无法通过重试成功,那么还需要更多的机制,来回滚操作 适用于可异步执行的业务,且后续操作无需回滚的业务 如果读者想要进一步研究事务消息,可参考 rocketmq,为了方便大家学习事务消息,DTM 也提供了简单实现 ◆ 最大努力通知 有一定的消息重复通知机制。因为接收通知方可能没有接收到通知,此时要有一定的机制对消息重复通知。消息校对机制。如果尽最大努力也没有通知到接收方,或者接收方消费消息后要再次消费,此时可由接收方主动向通知方查询消息信息来满足需求。前面介绍的的本地消息表和事务消息都属于可靠消息,与这里介绍的最大努力通知有什么不同? 可靠消息一致性,发起通知方需要保证将消息发出去,并且将消息发到接收通知方,消息的可靠性关键由发起通知方来保证。 最大努力通知,发起通知方尽最大的努力将业务处理结果通知为接收通知方,但是可能消息接收不到,此时需要接收通知方主动调用发起通知方的接口查询业务处理结果,通知的可靠性关键在接收通知方。 解决方案上,最大努力通知需要: 提供接口,让接受通知放能够通过接口查询业务处理结果 消息队列 ACK 机制,消息队列按照间隔 1min、5min、10min、30min、1h、2h、5h、10h 的方式,逐步拉大通知间隔 ,直到达到通知要求的时间窗口上限。之后不再通知 最大努力通知适用于业务通知类型,例如微信交易的结果,就是通过最大努力通知方式通知各个商户,既有回调通知,也有交易查询接口 ◆ AT 事务模式 ◆ 分布式事务中的网络异常 空回滚: 在没有调用 TCC 资源 Try 方法的情况下,调用了二阶段的 Cancel 方法,Cancel 方法需要识别出这是一个空回滚,然后直接返回成功。 出现原因是当一个分支事务所在服务宕机或网络异常,分支事务调用记录为失败,这个时候其实是没有执行 Try 阶段,当故障恢复后,分布式事务进行回滚则会调用二阶段的 Cancel 方法,从而形成空回滚。 幂等: 由于任何一个请求都可能出现网络异常,出现重复请求,所以所有的分布式事务分支,都需要保证幂等性 悬挂: 悬挂就是对于一个分布式事务,其二阶段 Cancel 接口比 Try 接口先执行。 出现原因是在 RPC 调用分支事务 try 时,先注册分支事务,再执行 RPC 调用,如果此时 RPC 调用的网络发生拥堵,RPC 超时以后,TM 就会通知 RM 回滚该分布式事务,可能回滚完成后,RPC 请求才到达参与者真正执行。 下面看一个网络异常的时序图,更好的理解上述几种问题 面对上述复杂的网络异常情况,目前看到各家建议的方案都是业务方通过唯一键,去查询相关联的操作是否已完成,如果已完成则直接返回成功。相关的判断逻辑较复杂,易出错,业务负担重。 在项目 DTM 中,出现了一种子事务屏障技术,使用该技术,能够达到这个效果,看示意图:
业务开发人员,在 busiCall 里面编写自己的相关逻辑,调用该函数。ThroughBarrierCall 保证,在空回滚、悬挂等场景下,busiCall 不会被调用;在业务被重复调用时,有幂等控制,保证只被提交一次。 子事务屏障会管理 TCC、SAGA、XA、事务消息等,也可以扩展到其他领域 子事务屏障技术的原理是,在本地数据库,建立分支事务状态表 sub_trans_barrier,唯一键为全局事务 id-子事务 id-子事务分支名称(try|confirm|cancel) 开启事务 如果是 Try 分支,则那么 insert ignore 插入 gid-branchid-try,如果成功插入,则调用屏障内逻辑 如果是 Confirm 分支,那么 insert ignore 插入 gid-branchid-confirm,如果成功插入,则调用屏障内逻辑 如果是 Cancel 分支,那么 insert ignore 插入 gid-branchid-try,再插入 gid-branchid-cancel,如果 try 未插入并且 cancel 插入成功,则调用屏障内逻辑 屏障内逻辑返回成功,提交事务,返回成功 屏障内逻辑返回错误,回滚事务,返回错误 在此机制下,解决了网络异常相关的问题 空补偿控制–如果 Try 没有执行,直接执行了 Cancel,那么 Cancel 插入 gid-branchid-try 会成功,不走屏障内的逻辑,保证了空补偿控制 幂等控制–任何一个分支都无法重复插入唯一键,保证了不会重复执行 防悬挂控制–Try 在 Cancel 之后执行,那么插入的 gid-branchid-try 不成功,就不执行,保证了防悬挂控制 对于 SAGA 事务,也是类似的机制。 子事务屏障技术,为 DTM 首创,它的意义在于设计简单易实现的算法,提供了简单易用的接口,在首创,它的意义在于设计简单易实现的算法,提供了简单易用的接口,在这两项的帮助下,开发人员彻底的从网络异常的处理中解放出来。 该技术目前需要搭配 DTM 事务管理器,目前 SDK 已经提供给 go 语言的开发者。其他语言的 sdk 正在规划中。对于其他的分布式事务框架,只要提供了合适的分布式事务信息,能够按照上述原理,快速实现该技术。 ◆ 总结 |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/24 5:38:33- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |