TCC分布式事务的设计、实现与示例

ikenchina · · 353 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

- 微信原文:点击 [TCC分布式事务的设计、实现与示例](https://mp.weixin.qq.com/s/0Jd-H7fvq2_EfhfP196uHg) - 可以关注我微信公众号,有很多优质文章 【技术闲聊吧】,或点开上面文章,关注作者即可 - 大家赏个star吧 : https://github.com/ikenchina/octopus # 理论 ## 论文 Pat Helland于2007年发表tcc论文《Life beyond Distributed Transactions: an Apostate’s Opinion》,提出了TCC的概念,在论文中,TCC还是以Tentative-Confirmation-Cancellation命名的,后来Atomikos公司改名为Try-Confirm-Cancel。 TCC事务相对于传统事务(XA, Two-Phase-Commit),其特征在于它不依赖资源管理器(RM)对XA的支持,而是通过对(由业务系统提供的)业务逻辑的接口调用来实现分布式事务。 ## 理论 TCC事务有一系列子事务构成,每个子事务所属的RM需要提供Try-Confirm-Cancel三个接口来给事务协调者调用。 TCC和2PC类似,也是分为两个阶段,Try阶段和Confirm或Cancel阶段。 - 第一阶段Try:进行资源检查与预留。尝试执行,完成所有业务一致性检查,预留业务资源,但不会对资源进行锁定。 - 第二阶段 - Confirm:确认,真正执行业务,处理Try 阶段预留的业务资源,Confirm 操作要求具备幂等设计,只要进入到Confirm阶段,则事务就已经处于提交状态了,所以Confirm 失败后需要进行重试直到成功为止。 - Cancel:取消,释放 Try 阶段预留的业务资源。Cancel 阶段的异常和 Confirm 阶段异常处理方案基本上一致,要求满足幂等设计。 > TCC不会对在数据资源层面对资源进行锁定,但是会进行业务层面的预留,以将资源层的加锁升级到业务层上,这样业务层可以灵活实现隔离性,达到准隔离性的同时提高并发性能。 ## 举例 仍然使用转账的例子:A和B账户余额都是100元,A转账30元给B。 这个TCC事务有两个子事务, - 子事务A:从A的账户减去30元,提交后A的账户余额为70元 - 子事务B:给B的账户添加30元,提交后B的账户余额为130元 需要在资源层将增加一个资源字段冻结余额 **执行事务**: - 第一阶段,Try: - 检查A的余额是否大于等于30元,利用资源层事务的原子性,在A的冻结余额中增加-30元,不修改A的余额 ,所以A的账户余额还是100元。其他事务看到的余额总数还是100元。 - 将B的冻结余额增加+30元,不修改B的账户余额,其他事务看到的B的账户余额仍然是100元。 - 第二阶段, - Confirm: - 将A的账户的冻结余额的-30元加到账户余额中,那么A的账户余额等于70元。 - 将B的账户的冻结余额的+30元加到账户余额中,B的账户余额等于130元。 - Cancel: - 将A的账户的冻结余额清空 - 将B的账户的冻结余额清空 **隔离性:** 事务执行期间,A和B的账户余额还是100元,但是如果其他事务需要并发修改A或B的账户余额,则需要考虑冻结余额。 - 如果其他事务需要修改A的账户余额,则需要查看冻结余额,发现已经冻结了-30元,则只有70元是可用的 - 如果其他事务需要修改B的账户余额,则需要查看冻结余额,发现已经冻结了+30元,则只有100元是可用的 或者事务执行期间,不允许其他事务修改A和B的的账户余额。 > 所以,将锁从资源层上升到业务层,隔离性更加灵活,需要如何实现隔离性,完全由业务决定,也避免了资源层并发带来的回滚。 ## 使用场景 - 适用于业务流程短的事务 - 隔离性较好:对中间状态有约束的业务 ### 优点 - 并发度较高,不需要像XA事务对资源进行锁定 - 隔离性比Saga好,取决于业务实现 ### 缺点 - 对业务有侵入性:需要实现Try,Confirm,Cancel接口 - 参与者需要实现幂等 # 设计 ## 基础概念 TCC事务由三种角色组成 - TCC事务提交者:事务发起者,在分布式事务中统称为AP - TCC事务协调者:接受AP的事务请求,管理TCC事务,在分布式事务中统称为事务协调者(TC : Transaction Coordinator) - TCC子事务参与者:协调者将子事务提交给参与者执行, 在分布式事务中统称为资源管理器(RM:Resource Manager)。参与者需要包证接口的幂等性 **事务状态** - committed : 已经提交。最终态 - aborted :已经回滚。最终态 - failed :回滚中或提交失败(可能重试中)。非终态 - prepared:提交中。非终态 ## TCC事务协调者的实现 TCC Service - 提供RESTful API,AP通过API进行事务提交或查询 - TCC service将请求封装成TCC事务对象,提交给TCC Executor执行 - API结果响应: - 如果是同步请求,则等待TCC Executor处理完成,再将结果封装成TccResponse 给AP - 如果是异步请求,则马上返回给AP - 通知:TC通知 AP TCC事务执行的结果 TCC Executor - TCC事务真正的执行者,管理TCC事务的状态转换,重试,持久化等等,不涉及与RM和AP的交互 - TCC子事务处理和回滚:TCC Executor并不处理子事务,而是将子事务操作写入Channel,由外层去向RM发送子事务请求 - TCC事务通知:TCC Executor不执行具体的通知操作,而是将操作写入Channel,由外层去执行AP - 数据持久化:TCC Executor将TCC数据存储到本地数据库中来保证数据的持久化,也同时依赖本地数据库的事务特性。 ## 事务流程 - 开启TCC事务:AP调用 TC的prepare接口,开启一个TCC事务 - 循环处理所有子事务:如果某个步骤失败,重试策略取决于AP - 注册子事务(分支事务):AP调用TC register接口,注册一个子事务,注册成功再执行下一步 - Try:AP调用RM的Try接口 - Confirm:如果所有子事务都成功注册和执行try,则AP调用TC的Confirm接口,告知事务可以执行提交 - TC循环调用所有参与子事务的RM的Confirm接口,如果返回失败则会一直重试指导成功 - Cancel:如果子事务执行失败,AP不进行重试则调用TC的Cancel接口取消TCC事务;或者达到TCC事务过期时间,TC会自行取消TCC事务。 - 循环调用所有子事务的RM,调用RM的Cancel接口 ## 异常情况处理 分布式事务实现的一个难点就是时序问题,主要体现在: - 服务器的时钟不同步 - 请求乱序 因此会产生一些不可预测的异常。 ### TCC事务过期 如果TCC事务过期,则TC需要先将事务的状态标记为"需要Cancel"状态,再调用RM的Cancel接口来取消子事务。 如果后续TC接受到此TCC事务的Confirm请求,TC应先查看本地数据库中此事务状态,如果已经处于Cancel或者已经Cancel,则应该拒绝Confirm请求。 同时,TC对数据库中事务的状态修改,应该使用数据库事务来确保隔离性和一致性,因为可能有多个TC同时存在。 ### 回滚异常 异常流程如下: - TC向RM发送Try请求 - 由于网络原因Try请求仍然处于发送中,没有到达RM - AP调用TC取消TCC事务,或者TCC事务过期,TC自行取消 - TC调用RM的Cancel接口取消子事务 - 异常点1 :RM收到Cancel请求,发现此子事务没有执行过Try,产生异常 - 异常点2 :当RM收到Cancel请求后,之前由于网络原因阻塞的Try请求到达RM,如果RM执行这个Try,则会产生数据不一致的异常。 - 异常点3:由于重试策略,导致AP向RM发送了多于1次的Try请求,或者TC向RM发送了多次Confirm或者Cancel请求。 所以,为了避免上面异常情况,需要进行如下检查 - RM收到Try操作, 检查是否有Try记录 - 如果有记录:如果已经执行过Try,直接返回成功,确保幂等性 - 如果没有Try记录,则检查是否有Cancel记录, - 如果有Cancel记录,则拒绝执行Try - 如果没有Cancel记录,则执行Try,且更改子事务状态为"已经执行try"。 - RM收到Cancel操作,查看此子事务是否有Cancel记录, - 如果有Cancel记录,则直接返回Cancel成功,确保幂等性 - 如果没有Cancel记录,则检查是否有Try记录 - 如果有Try记录,则执行Cancel动作 - 如果没有Try记录,则将此Cancel请求记录下来,如果后续收到Try请求,则应该拒绝Try请求 ### 本地事务 无论是RM还是TC或AP,在修改其数据时要考虑时序问题和时钟漂移问题导致的乱序,利用本地数据库的事务隔离(可序列化级别)特性来检查事务状态和修改状态。 如: RM收到Try请求后,需要检查是否有Cancel记录或Try记录,没有才能需要执行Try。检查和执行需要在一个事务中,避免其他线程或进程同时对子事务进行Cancel或者Try修改。 # 开发示例 以银行转账为示例,演示如果通过SDK来实现一个tcc事务。 此演示使用了SDK的高级API,屏蔽了各种异常情况的处理,提高了开发的效率。 **开发示例** https://github.com/ikenchina/octopus/blob/master/README_tcc_demo.md **SDK** client sdk实现了AP和RM的sdk封装,方便进行tcc开发。 client SDK:https://github.com/ikenchina/octopus/blob/master/README_tcc_sdk.md **API设计** https://github.com/ikenchina/octopus/octopus/README_tcc_api.md

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

353 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传