TCC和SAGA是微服务架构中常见的两种处理分布式事务的模式,个人最先接触的是TCC,近几年来,工作中使用多的是SAGA。TCC是“Try-Confirm-Cancel”的首字母缩写,它的核心思想是在Try阶段进行各应用服务处理资源锁定或预留,如果Try均成功,则使用预留资源完成各服务的Confirm操作,如果Try出现失败,则进行Cancel操作,释放各服务之前锁定或预留的资源。而SAGA的思路更简单些,它要求每个应用服务均有自身对应的补偿服务,在运行时,先依次执行应用服务,如果遇到失败,则沿之前执行路径倒序执行应用补偿服务。有一种观点认为SAGA是去掉Confirm的简化版TCC,从应用现状看大多数方面确实是这样的,不过,SAGA是1987年提出的【1】,TCC要到2007年才被提出【2】,SAGA出现得更早,并且SAGA最初是为了解决长活应用对数据库资源的过渡占用问题提出的,开始跟分布式事务的关系倒不大。
账户转账TCC与SAGA处理过程举例虽然都是实现分布式事务的模式,但与传统事务ACID的要求相比,TCC和SAGA在原子性、一致性、隔离性上是有所妥协的,在原子性方面,TCC和SAGA的执行底层是一系列原子事务服务的组合,在一致性方面,只能保证绝大多数情况下准实时的一致性,在隔离性方面,存在脏读、幻读等问题。通常应用对隔离性问题更加敏感,以上图中A账户向B账户转账100元为例,在TCC模式下,假设Try后因B账户问题交易Cancel,期间其他应用读取A账户余额时就脏读了,而在SAGA模式下,假设因B账户问题导致回滚,期间其他应用读取A账户余额时也脏读了,并且会幻读A账户动账明细。两者相比较,TCC比SAGA在隔离性方面的处理要更好一些。但通过以上例子也可以看出,TCC模式的应用实现要复杂很多。
考虑TCC模式对人员技能水平和项目技术管控能力的要求,笔者建议一般项目在实施时优先选择SAGA模式。至于SAGA隔离性方面,笔者遇到过的一个确实需要解决的问题,场景是这样的:客户的ERP系统会通过银企互通渠道以固定时间间隔查询客户账户动账明细,但如上文所述,此间并发执行的交易如果出现业务错误或者技术错误,交易会进行SAGA回滚,而回滚处理会删除之前步骤产生的动账明细,导致客户出现幻读。因客户依赖账户动账明细进行自身业务处理,这种情况是客户不能容忍的。
那么,如何解决这个问题呢?笔者建议的方式是交易过程中生成账户明细时将状态置为处理中,该状态不支持查询,待交易执行完毕后发送异步消息,触发特定任务修改账户明细状态为正常,以支持查询。这种方式本质是通过牺牲一定的时效性来保证数据的准确可靠。可能业界更常用的办法是引入分布式锁来解决隔离性问题,但鉴于分布式锁本身就需要新开发一套机制来实现,同时引入分布式锁会给系统的单账户/单客户并发处理能力提升带来比较大的限制,另外在分布式锁释放出现问题的情况下一些账户/客户的处理将出现问题,综合这些因素,一般情况下不建议引入分布式锁来解决上述隔离性问题。
参考文献:
【1】SAGAS,https://www.cs.cornell.edu/andru/cs711/2002fa/reading/sagas.pdf
【2】Helland P .Life beyond distributed transactions[J].Communications of the Acm, 2017, 60(2):46-54.DOI:10.1145/3009826.
网友评论