当前位置:   article > 正文

TCC真没这么简单,一文讲透|分布式事务系列(三)

tcc

本文从两个场景说起,详细描述了TCC的详细过程,以及对比2PC有什么区别,适用什么样的场景。

点击上方“后端开发技术”,选择“设为星标” ,优质资源及时送达

在面试前复习 TCC 的时候你是不是这样做的:百度TCC关键词,随便找了篇文章,查询到他有try、confirm、Cancel 三个阶段,业务侵入度高,和两阶段差不多。复习完毕。

如果你是这样去理解和复习的,只能说对 TCC 的理解太不到位了,真的有必要耐心看完这篇文章。

TCC

有些人可能觉得 TCC 和两阶段提交非常相似,无法区分。首先强调,TCC是一种最终一致性方案,他并不是强一致性。如果你不了解两阶段提交,请先看这篇。

045c17acf8eb63fc9cbad7a83b651424.jpeg

分布式事务,强一致性方案有哪些?|分布式事务系列(二)


这篇文章我们来探讨下 TCC 到底是什么,他用来解决什么问题?在继续阅读之前,我先抛出两个问题。

一个面试中的问题

在某大厂面试的时候,面试官问了我一个问题:由于我介绍的是一个支付项目,业务系统调用支付系统扣款成功后采用本地事务状态表(最终一致性)的方案通知业务系统。由于我们的系统QPS并不高,所以我自认为这样的方案是没问题的。

但是面试官问我,如果扣款成功后订单系统数据库写入不可用怎么办?虽然用户支付成功,但是即使有短暂几分钟的写入不可用也会导致客诉暴涨,你怎么解决这个问题?

这让我意识到,如果在高并发场景下这个方案是有问题的。本地事务状态表的缺点就是及时性不够,当然这个可以通过再本地事务结束后及时发起对业务的通知解决。但是有个问题无法解决,本地事务状态表都是基于本地业务执行成功后,下游依赖业务也会成功。如果下游系统发生不可用问题,我们的本地事物状态表中状态将阻塞在这里,出现上述支付扣款后一直无法通知到订单的情况。

电商超卖问题

除了上述面试问题,还有一个电商中很常见的超卖问题。

还是以电商场景下的余额支付、扣库存为例。如果我们采用可靠消息队列或者本地事务表的方案,用户购买了一瓶可乐,余额扣款成功后发送消息到库存系统,库存系统对可乐的库存减1。如果是只有一个用户买并且库存充足的情况下这是没有问题的,但是如果此时有3个用户在购买可乐,但是库存仅剩 1 瓶,支付前使用接口检查剩余库存的时候是通过的。但是等到支付成功,扣减库存的消息同步到库存服务的时候系统就会出现超卖 2 瓶的情况。

c0706d432fc1a4d309bc38af8456ac6b.png

之所以出现上述问题,是因为创建订单在业务上是用户之间隔离的,业务上不会有资源的竞争,但是库存数据是一种共享资源,多个用户同时购买同样的商品就会出现并发问题,所以需要不同的线程之间事务隔离。

实现隔离性可以使用我们之前提到的强一致性方案 两阶段提交,但是在电商场景高并发下,两阶段提交持有资源时间过长并不合适。强一致性性能太低,消息队列方案由无法资源隔离,怎么办呢?TCC 方案就是为这种场景而生的。

什么是 TCC

TCC 是“Try-Confirm-Cancel”三个单词的缩写,是由数据库专家 Pat Helland 在 2007 年撰写的论文《Life beyond Distributed Transactions: An Apostate’s Opinion》中提出。一提到 TCC 我们都知道它是一种业务侵入较强的分布式事务方案,要求业务处理过程必须拆分为“try ”和“ confirm/cancel”两个阶段,并且需要分别提供这两个阶段所涉及三个步骤的接口。

  • Try :尝试执行阶段,完成所有业务可执行性的检查(保障一致性),并且预留好全部需用到的业务资源(保障隔离性)。

  • Confirm :确认执行阶段,不进行任何业务检查,直接使用 Try 阶段准备的资源来完成业务处理(也就是说业务上理论一定可以执行成功)。由于分布式环境下的不可靠性,Confirm 阶段可能会重复执行,因此本阶段所执行的操作需要具备幂等性。

  • Cancel:取消执行阶段,释放 Try 阶段预留的业务资源。由于分布式环境下的不可靠性,Cancel 阶段可能会重复执行,也需要满足幂等性。

具体时序图如下:

07d1e944b455423a61846cc8932062ee.png

这三个阶段的特点如下:

流程特点
Try预留业务资源(不锁定资源,独立事物)
Confirm确认提交资源(需要重试,最终一致性)
Cancel释放资源(需要重试,最终一致性)

如何解决前文两个问题

订单状态不更新问题

第一个问题,如何解决扣款成功订单状态迟迟不更新:

  1. 如果采用TCC方案,首先本地创建事务,生成事务 ID,记录在活动日志中,此时状态为Try。

  2. 订单在扣款和通知订单状态变更会在Try阶段进行检查,首先在用户的余额系统锁定订单金额,然后通知订单状态变为支付待确认。

  3. 如果订单库此时写入不可用,那么Try阶段订单服务会失败,活动日志状态变更为 Cancel。

  4. 进入Cancel阶段后,余额服务将锁定的金额释放,通知订单支付不成功,如果任意环节失败,可以用最终一致性方案不断尝试。

这样,上述问题通过余额回退的方式得到化解。

超卖问题

第二个问题,如何解决电商超卖的问题:

  1. 用户发起支付请求,购买一瓶可乐。

  2. 创建事务,生成事务 ID,记录在活动日志中,进入 Try 阶段:

    余额服务尝试锁定金额,库存服务尝试锁定库存资源。两者有任意失败或者接口超时活动日志的状态记录为 Cancel,将进入 Cancel阶段;全部成功则进入 Confirm 阶段,活动日志的状态记录为 Confirm。

  3. Confirm 阶段,说明Try全部成功:余额服务执行扣减金额,库存执行扣减库存。

    Confirm 阶段如果全部完成,事务正常结束。如果任何一个环节出现异常,不论是业务异常或者网络异常,都将根据活动日志中的记录,重复执行该服务的 Confirm 操作,实现最终一致。

  4. Cancel 阶段,说明Try 阶段有服务超时或者执行失败:撤销用户被锁定的金额,解锁被占用的库存。

    Cancel 阶段如果全部完成,事务以执行回滚结束。如果在 Cancel 时任意服务超时或者失败,都将根据活动日志中的记录,重复执行该服务的 Cancel 操作,实现最终一致性。

假设可乐只剩 1 瓶,因为我们在第 Try 阶段首先执行余额锁定和库存扣减,如果用户A扣款成功,并且锁定库存成功,此时可乐的可购买库存为0。当用户B并发购买,即使余额锁定成功,但是检查库存时发现已经库存不足,将通知余额解锁金额,超卖问题由此解决。

f96a3b2fea5533fe27a9cba22018dbd3.png

TCC与2PC对比

正因为 TCC 也分为了 Try 和 Confirm/Cancel 两个阶段,所以很多人对此和两阶段提交产生了混淆,这里用两个表格列出他们的异同。

相同:

TCC两阶段提交
可分为 Try 和 Confirm/Cancel 两个阶段可以分为投票阶段和提交阶段 两个阶段
在Try阶段占用资源在第一阶段占用资源

不同:

TCC两阶段提交
实现了最终一致性刚性事务,实现了强一致性
服务的代码逻辑层面实现,需要对三种操作分别编码,有业务侵入,开发成本更高底层数据库支持两阶段协议,无业务侵入
第一阶段,Try 阶段会直接提交事物,只会短暂持有资源锁,性能较高第一阶段会持续锁定资源并持有锁,事物不提交,造成服务阻塞,性能低
第二阶段的 Confirm 和 Cancel 如果出现失败,会利用最终一致性方案不断重试第二阶段如果有失败,会造成系统之间的数据不一致
TCC 的每个步骤在数据库层面都是一个独立事务两阶段的两个步骤和起来是一个完整的事务

适用场景

说了这么多,我们应该在什么场景下使用 TCC 呢?我做了些总结。

  1. 当常规最终一致性方案无法满足,需要更强的一致性。

  2. 当业务对实时性要求高。

  3. 当业务涉及到资源争夺,需要更高的隔离性。

  4. 当业务在满足一致性和隔离性的时候,还需要更好的性能表现。

当你的业务遇到这些问题的时候,那就可以考虑TCC了,比如涉及到资金数据,银行业务,金融业务,涉及到交易、支付、账务都可以考虑。

但是上述优点都是有代价的,TCC 对于业务的侵入性非常强,业务逻辑的每个分支都需要实现try、confirm、cancel三个操作。难度也比较大,需要根据不同的失败原因实现不同的回滚策略,有更高的开发成本和更换事务实现方案的替换成本。我们可以基于某些分布式事务中间件(譬如阿里开源的Seata)去完成,尽量减轻一些编码工作量。

加入讨论群是升职加薪第一步!

回复:加群

e50721ecedf5dbad1693ee8fb6a4caf0.jpeg

点赞是一种美德,如对您有帮助,欢迎评论和分享,感谢阅读!

从二叉查找树到B*树,一文搞懂搜索树的演进!|金三银四系列

2023-03-25

eddb20b4a8f32b9c1b1a01dc2e3a1220.jpeg

面试官:会SQL调优,那你知道索引合并吗?|金三银四系列

2023-03-21

46c3825ca18adeecb6f20319874aca18.jpeg

面试官:你是如何预防多线程死锁的?|金三银四系列

2023-03-16

130e8fbcc8d0c692bba47df9eb98663c.jpeg
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Guff_9hys/article/detail/1011466
推荐阅读
相关标签
  

闽ICP备14008679号