用Python轻松完成一个分布式事务TCC,保姆级教程

用Python轻松完成一个分布式事务TCC,保姆级教程

作者:佚名 2021-10-25 10:33:29

开发

前端

分布式 分布式事务就是指事务的发起者、资源及资源管理器和事务协调者分别位于分布式系统的不同节点之上。

 

什么是分布式事务?银行跨行转账业务是一个典型分布式事务场景,假设A需要跨行转账给B,那么就涉及两个银行的数据,无法通过一个数据库的本地事务保证转账的ACID,只能够通过分布式事务来解决。

分布式事务就是指事务的发起者、资源及资源管理器和事务协调者分别位于分布式系统的不同节点之上。在上述转账的业务中,用户A-100操作和用户B+100操作不是位于同一个节点上。本质上来说,分布式事务就是为了保证在分布式场景下,数据操作的正确执行。

什么是TCC分布式事务,TCC是Try、Confirm、Cancel三个词语的缩写,最早是由 Pat Helland 于 2007 年发表的一篇名为《Life beyond Distributed Transactions:an Apostate’s Opinion》的论文提出。

TCC组成

TCC分为3个阶段

  • Try 阶段:尝试执行,完成所有业务检查(一致性), 预留必须业务资源(准隔离性)
  • Confirm 阶段:如果所有分支的Try都成功了,则走到Confirm阶段。Confirm真正执行业务,不作任何业务检查,只使用 Try 阶段预留的业务资源
  • Cancel 阶段:如果所有分支的Try有一个失败了,则走到Cancel阶段。Cancel释放 Try 阶段预留的业务资源。

TCC分布式事务里,有3个角色,与经典的XA分布式事务一样:

  • AP/应用程序,发起全局事务,定义全局事务包含哪些事务分支
  • RM/资源管理器,负责分支事务各项资源的管理
  • TM/事务管理器,负责协调全局事务的正确执行,包括Confirm,Cancel的执行,并处理网络异常

如果我们要进行一个类似于银行跨行转账的业务,转出(TransOut)和转入(TransIn)分别在不同的微服务里,一个成功完成的TCC事务典型的时序图如下:

TCC实践

对于前面的跨行转账操作,最简单的做法是,在Try阶段调整余额,在Cancel阶段反向调整余额,Confirm阶段则空操作。这么做带来的问题是,如果A扣款成功,金额转入B失败,最后回滚,把A的余额调整为初始值。在这个过程中如果A发现自己的余额被扣减了,但是收款方B迟迟没有收到余额,那么会对A造成困扰。

更好的做法是,Try阶段冻结A转账的金额,Confirm进行实际的扣款,Cancel进行资金解冻,这样用户在任何一个阶段,看到的数据都是清晰明了的。

下面我们进行一个TCC事务的具体开发

目前可用于TCC的开源框架,主要为Java语言,其中以seata为代表。我们的例子采用Python语言,使用的分布式事务框架为https://github.com/yedf/dtm,它对分布式事务的支持非常优雅。下面来详细讲解TCC的组成

我们首先创建两张表,一张是用户余额表,一张是冻结资金表,建表语句如下:

  1. CREATE TABLE dtm_busi.`user_account` ( 
  2.   `id` int(11) AUTO_INCREMENT PRIMARY KEY, 
  3.   `user_id` int(11) not NULL UNIQUE , 
  4.   `balance` decimal(10,2) NOT NULL DEFAULT '0.00'
  5.   `create_time` datetime DEFAULT now(), 
  6.   `update_time` datetime DEFAULT now() 
  7. ); 
  8.  
  9. CREATE TABLE dtm_busi.`user_account_trading` ( 
  10.   `id` int(11) AUTO_INCREMENT PRIMARY KEY, 
  11.   `user_id` int(11) not NULL UNIQUE , 
  12.   `trading_balance` decimal(10,2) NOT NULL DEFAULT '0.00'
  13.   `create_time` datetime DEFAULT now(), 
  14.   `update_time` datetime DEFAULT now() 
  15. ); 

trading表中,trading_balance记录正在交易的金额。

我们先编写核心代码,冻结/解冻资金操作,会检查约束balance+trading_balance >= 0,如果约束不成立,执行失败

  1. def tcc_adjust_trading(cursor, uid, amount): 
  2.   affected = utils.sqlexec(cursor, "update dtm_busi.user_account_trading set trading_balance=trading_balance + %d where user_id=%d and trading_balance + %d + (select balance from dtm_busi.user_account where id=%d) >= 0" % (amount, uid, amount, uid)) 
  3.   if affected == 0
  4.     raise Exception("update error, maybe balance not enough"

然后是调整余额

  1. def tcc_adjust_balance(cursor, uid, amount): 
  2.   utils.sqlexec(cursor, "update dtm_busi.user_account_trading set trading_balance = trading_balance+ %d where user_id=%d" %( -amount, uid)) 
  3.   utils.sqlexec(cursor, "update dtm_busi.user_account set balance=balance+%d where user_id=%d" %(amount, uid)) 

下面我们来编写具体的Try/Confirm/Cancel的处理函数

  1. @app.post("/api/TransOutTry"
  2. def trans_out_try(): 
  3.   # 事务以及异常处理 
  4.   tcc_adjust_trading(c, out_uid, -30
  5.   return {"dtm_result""SUCCESS"
  6.  
  7. @app.post("/api/TransOutConfirm"
  8. def trans_out_confirm(): 
  9.   # 事务以及异常处理 
  10.   tcc_adjust_balance(c, out_uid, -30
  11.   return {"dtm_result""SUCCESS"
  12.  
  13. @app.post("/api/TransOutCancel"
  14. def trans_out_cancel(): 
  15.   # 事务以及异常处理 
  16.   tcc_adjust_trading(c, out_uid, 30
  17.   return {"dtm_result""SUCCESS"
  18.  
  19. @app.post("/api/TransInTry"
  20. def trans_in_try(): 
  21.   # 事务以及异常处理 
  22.   tcc_adjust_trading(c, in_uid, 30
  23.   return {"dtm_result""SUCCESS"
  24.  
  25. @app.post("/api/TransInConfirm"
  26. def trans_in_confirm(): 
  27.   # 事务以及异常处理 
  28.   tcc_adjust_balance(c, in_uid, 30
  29.   return {"dtm_result""SUCCESS"
  30.  
  31. @app.post("/api/TransInCancel"
  32. def trans_in_cancel(): 
  33.   # 事务以及异常处理 
  34.   tcc_adjust_trading(c, in_uid, -30
  35.   return {"dtm_result""SUCCESS"

到此各个子事务的处理函数已经OK了,然后是开启TCC事务,进行分支调用

  1. @app.get("/api/fireTcc"
  2. def fire_tcc(): 
  3.     # 发起tcc事务 
  4.     gid = tcc.tcc_global_transaction(dtm, utils.gen_gid(dtm), tcc_trans) 
  5.     return {"gid": gid} 
  6.  
  7. # tcc事务的具体处理 
  8. def tcc_trans(t): 
  9.     req = {"amount"30} # 业务请求的负荷 
  10.     # 调用转出服务的Try|Confirm|Cancel 
  11.     t.call_branch(req, svc + "/TransOutTry", svc + "/TransOutConfirm", svc + "/TransOutCancel"
  12.     # 调用转入服务的Try|Confirm|Cancel 
  13.     t.call_branch(req, svc + "/TransInTry", svc + "/TransInConfirm", svc + "/TransInCancel"

至此,一个完整的TCC分布式事务编写完成。

如果您想要完整运行一个成功的示例,那么按照dtmcli-py-sample项目的说明tcc的例子即可

TCC的回滚

假如银行将金额准备转入用户2时,发现用户2的账户异常,返回失败,会怎么样?我们修改代码,模拟这种情况:

  1. @app.post("/api/TransInTry"
  2. def trans_in_try(): 
  3.   # 事务以及异常处理 
  4.   tcc_adjust_trading(c, in_uid, 30
  5.   return {"dtm_result""FAILURE"

这是事务失败交互的时序图

这个跟成功的TCC差别就在于,当某个子事务返回失败后,后续就回滚全局事务,调用各个子事务的Cancel操作,保证全局事务全部回滚。

TCC网络异常

TCC在整个全局事务的过程中,可能发生各类网络异常情况,典型的是空回滚、幂等、悬挂,由于TCC的异常情况,和SAGA、可靠消息等事务模式有相近的地方,因此我们把所有异常的解决方案统统放在这篇文章分布式事务最经典的七种解决方案的异常处理章节进行讲解

小结

在这篇文章里,我们介绍了TCC的理论知识,也通过一个例子,完整给出了编写一个TCC事务的过程,涵盖了正常成功完成,以及成功回滚的情况。相信读者通过这边文章,对TCC已经有了深入的理解。

文章来源网络,作者:管理,如若转载,请注明出处:https://shuyeidc.com/wp/308053.html<

(0)
管理的头像管理
上一篇2025-05-27 10:01
下一篇 2025-05-27 10:02

相关推荐

  • 骨干网络体系结构能干什么?骨干网络体系结构的作用

    骨干网络体系结构是现代信息社会的“超级高速公路网”,它通过分层设计、冗余备份和智能调度,确保海量数据在全球范围内高速、稳定、安全地传输,是支撑云计算、物联网及人工智能应用的底层基石,想象一下,如果你把互联网比作一个巨大的城市交通系统,那么骨干网络就是连接各个城市的主干道和立交桥,没有它,你的每一次微信发送、每一……

    2026-06-18
    0
  • 高io数据库可以干什么用?高io数据库适合什么场景

    高IO数据库的核心价值在于通过极高的读写吞吐量,解决海量数据场景下的性能瓶颈,是支撑高并发交易、实时分析及大规模内容分发的关键基础设施,在数字化转型的深水区,数据不再仅仅是静态的记录,而是流动的资产,传统的机械硬盘或普通SSD早已无法满足现代应用对速度的极致追求,高IO(Input/Output)数据库,就是那……

    2026-06-18
    0
  • 高io服务器性能如何?高io服务器适合什么场景

    高IO服务器并非单纯指代某种硬件,而是指在随机读写、高并发连接及小文件处理场景下,具备极致IOPS(每秒输入输出操作次数)和低延迟特性的计算资源,它是支撑现代高并发应用稳定运行的核心基石,在2026年的数字化浪潮中,业务负载早已从简单的静态页面展示演变为复杂的实时数据处理,许多开发者在排查系统瓶颈时,往往忽略了……

    2026-06-18
    0
  • 隔离网络空间哪里便宜?国内隔离网络空间价格

    隔离网络空间并没有统一的“便宜”标准,其成本高度取决于物理隔离等级、带宽需求及安全合规要求,通常物理网闸方案初期投入较高但长期运维成本低,而逻辑隔离方案虽初期便宜但存在潜在安全风险,建议根据业务敏感度选择混合隔离架构以平衡成本与安全,在数字化时代,企业构建独立网络环境的需求日益增长,但“隔离网络空间哪里便宜”这……

    2026-06-18
    0
  • 骨干网络体系结构设备为何故障?常见原因有哪些

    骨干网络体系结构设备故障的核心原因通常归结为硬件老化、配置错误、物理链路中断及外部攻击四大类,其中电源模块失效与光模块性能衰减是占比最高的隐性故障源,骨干网作为数字经济的“大动脉”,其稳定性直接关乎国计民生,当核心路由器或交换机出现丢包、震荡甚至宕机时,运维人员往往面临巨大的压力,很多人第一反应是检查软件配置……

    2026-06-18
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注