分布式场景下的事务管理应用

29 阅读5分钟

场景

A需要转100元给 B,那么需要A的余额-100元,给B的余额+100元,整个转账要保证,A-100和B+100同时成功,或者同时失败。

数据库事务

假设业务单一,需求可以在单个服务,同一个数据库进行修改,完成转账。可以利用数据库事务,保证转账业务的正确完成。

分布式事务

A需要转100元给B,A、B分属不同银行,如何保证一致性?

  • 跨数据库:AB使用同一套系统,但是分属不同的数据库
  • 跨服务:AB转账服务使用不同的金流系统,有各自的数据库
  • 混合:AB转账服务使用不同的金流系统,转账需操作到不同的数据库

原生方案(XA)和缺点

  • 跨数据库XA事务:T0在两个事务提交的时间差查询,结果为A+B+100 (不符合强一致性)
  • T0 + XA事务 + 隔离级别设定到 Serializable:结果为A+B (似乎OK)
  • T1 + XA事务 + 隔离级别设定到 Serializable:结果为A-100+B
  • 查询也使用XA + 排它锁:查询和更新全都上锁,牺牲性能和体验保持一致性

很明显这种方案下的强一致,缺点非常多。互联网应用大多是读多写少,在数据库中,原先的读可以通过多版本技术,不被锁定,快速出结果,而上述这种强一致方案:

  • 一方面效率极低,所有有数据交集的数据库读读、读写、写写都必须串行执行。
  • 另一方面,开发人员进行数据库多个数据查询时,也可能发生死锁,要么让开发人员小心排好访问顺序,要么接受死锁

最终一致性方案

在分布式事务进行的过程中,一致性是无法得到保证的,但是分布式事务完成之后,一致性是没问题的,严格遵守的。因此我们将分布式事务方案称为最终一致性方案。

DTM和应用

DTM

  • RM-资源管理器:RM是一个应用服务,负责管理全局事务中的本地事务,他通常会连接到一个数据库,负责相关数据的修改、提交、回滚、补偿等操作。例如在前面的这个SAGA事务中,步骤2、3中被调用的TransIn,TransOut服务都是RM,业务上负责A、B账户余额的修改
  • AP-应用程序:AP是一个应用服务,负责全局事务的编排,他会注册全局事务,注册子事务,调用RM接口。例如在前面的这个SAGA事务中,发起步骤1的是AP,它编排了一个包含TransOut、TransIn的全局事务,然后提交给TM
  • TM-事务管理器:TM就是DTM服务,负责全局事务的管理,每个全局事务都注册到TM,每个事务分支也注册到TM。TM会协调所有的RM,将同一个全局事务的不同分支,全部提交或全部回滚。例如在前面的SAGA事务中,TM在步骤2、3中调用了各个RM,在步骤4中,完成这个全局事务

特性

  • 跨语言
  • 高可用
  • 多进程同时轮询

应用

saga模式在下单事务中的应用举例

核心思想是将长事务拆分为多个短事务,由Saga事务协调器协调,如果每个短事务都成功提交完成,那么全局事务就正常完成,如果某个步骤失败,则根据相反顺序一次调用补偿操作

异常问题

子事务乱序问题

一般情况下,一个TCC回滚时的执行顺序是,先执行完Try,再执行Cancel,但是由于N,则有可能Try的网络延迟大,导致先执行Cancel,再执行Try。

  • 空补偿: Cancel执行时,Try未执行,事务分支的Cancel操作需要判断出Try未执行,这时需要忽略Cancel中的业务数据更新,直接返回
  • 悬挂: Try执行时,Cancel已执行完成,事务分支的Try操作需要判断出Cancel已执行,这时需要忽略Try中的业务数据更新,直接返回

分布式事务还有一类需要处理的常见问题,就是重复请求

  • 幂等: 由于任何一个请求都可能出现网络异常,出现重复请求,所有的分布式事务分支操作,都需要保证幂等性

  • 业务处理请求4的时候,Cancel在Try之前执行,需要处理空回滚
  • 业务处理请求6的时候,Cancel重复执行,需要幂等
  • 业务处理请求8的时候,Try在Cancel后执行,需要处理悬挂

异常处理

子事务屏障

  1. 开启本地事务
  2. 对于当前操作op(try|confirm|cancel),insert ignore一条数据gid-branchid-op,如果插入不成功,提交事务返回成功(常见的幂等控制方法)
  3. 如果当前操作是cancel,那么在 insert ignore 一条数据 gid-branchid-try,如果插入成功(注意是成功),则提交事务返回成功
  4. 调用屏障内的业务逻辑,如果业务返回成功,则提交事务返回成功;如果业务返回失败,则回滚事务返回失败

经验与探讨