分布式事物设计与实践

数据一致性定义

  • 任何人
  • 任何时间
  • 任何地点
  • 任何接入方式
  • 任何服务
  • 数据都是一致的

数据不一致产生的原因

  • 数据分散在多处
    • 多个DB
    • DB和缓存
  • 二手交易平台案例
    • 用户,交易,商品等功能

分布式事物产生的原因

刚开始是一个单体进程

经过演变,单体式服务演变成微服务,每个服务都是单独的进程

在用户请求量大的时候,为了缓解数据库的压力,添加了分布式缓存

分布式事物案例

电商平台购买商品

下单->减库存->支付

这就是分布式事物问题,当APP要买东西,这个操作会涉及到多个服务,意味着要操作多个数据库,这样本地事物就无法保证数据的一致性,所以就产生了分布式事物问题.

分布式事物场景

  • 电商下单场景
    • 下单
    • 发送消息到MQ
  • 一致性保证
    • 本地事物
      • 下单操作
      • 发送MQ消息操作
      • 放进一个本地事物

上述做法有什么问题?

问题:如果发送消息超时了,你是不知道MQ的返回结果是成功和失败的,,timeout这操作不是一个原子的

分布式事物分类

  • 刚性分布式事物
    • 强一致性
    • XA模型
    • CAP
      • CP
  • 柔性分布式事物
    • 最终一致性
    • CAP,BASE理论
      • AP

刚性分布式事物

满足传统事物特性

ACID( Atomicity-原子性, Consistency-一致性,Isolation-隔离性,Durability-持久性)

XA模型

  • XA是X/Open CAE Specification(Distributed Transaction Processing)模型中定义,XA规范由AP,RM,TM组成
  • 其中应用程序(Application Program简称AP),AP定义事物边界(定义事物开始和结束)并访问边界事物内的资源
  • 资源管理器(Resource Manager简称RM),RM管理计算机共享的资源,资源及数据库等
  • 事物管理器(Transaction Manager,简称TM),负责管理全局事物,分配事物唯一标识,监控事物的执行进度,并负责事物的提交,,回滚,失败恢复等

2PC(两阶段提交-XA规范标准实现)

  • 案例
    • 组织爬山
  • 过程
    • 二阶段提交,是XA规范的标准实现
    • TM发起prepare投票
    • RM都同意后,TM再发起Commit
    • Commit 过程出现宕机等异常,节点服务重启后,根据XA recover 再次进行commit补偿
  • 缺点
    • 同步阻塞模型
    • 数据库资源锁定时间过长
    • 全局锁(隔离级别-串行化),并发低
    • 不适合长事物场景

柔性分布式事物

  • CAP
    • 分布式环境下P一定需要,CA权衡折中
  • BASE理论
    • Basically Available-基本可用
    • Soft state 柔性状态
    • Eventual consistency 最终一致性
  • 架构思考
    • 柔性事物是对XA协议的妥协,他通过降低强一致性要求,从而降低数据库资源锁定时间,提升可用性
  • 架构经典实现
    • TCC模型
    • Saga模型

TCC模型

  • Try-confirm-cancel
  • TCC模型完全交由业务实现,每个子业务都需要实现Try-Confirm-cancel接口,对业务侵入大
    • 资源锁定交由业务方
  • try
    • 尝试执行业务,完成所有检查,预留必要的业务资源
  • confirm
    • 真正执行业务,不再做业务检查
  • Cancel
    • 释放Try阶段预留的业务资源
  • 案例
    • 汇款服务,收款服务案例
      • A用户向B用户汇款500元
    • 汇款服务
      • try
        • 检查A账户的有效性,及查看A账户的状态是否为"转账中"或者"冻结"
        • 检查A账户余额是否充足
        • 从A账户中扣减500元,并将状态设置为转账中
        • 预留扣减资源,将从A往B账户转账500元这个事件存入消息或者日志中
      • confirm
        • 不做任何操作
      • cancel
        • A账户增加500元
        • 从日志或者消息中,释放扣减资源
    • 收款服务
      • try
        • 检查B账户是否有效
      • confirm
        • 读取日志或消息,B账户增加500元
        • 从日志或者消息中,释放扣减资源
      • cancel
        • 不做任何操作

Saga模型

  • 起源于1987年Hector & Kenneth发表的论文Sagas
  • Saga模型把一个分布式事物拆分为多个本地事物,每个本地事物都有相应的执行模块和补偿模块(对应TCC中的confirm和cancel)
  • 当Saga事物中任意一个本地事物出错时,可以通过调用相关的补偿方法恢复之前的事物,到达事物最终一致性
  • 当每个Saga子事物T1,T2,....TN都有对应的补偿定义C1,C2,....CN-1,那么Saga系统可以保证
    • 子事物序列T1,T2,.....TN得以完成(最佳情况)
    • 或者序列T1,T2,...TJ,CJ-1,..., C2,C1,0<J<N,得以完成
  • Saga隔离性
    • 业务层控制并发
      • 在应用层加锁
      • 应用层预先冻结资源等
  • Saga恢复方式
    • 向后恢复,补偿所有已完成的事物,如果任意子事物的失败
    • 向前恢复,重试失败的事物,假设每个子事物最终都会成功

刚性分布式事物VS柔性分布式事物

我们如何实践

  • 问题通用解决思路
    • 解决这个问题本身
    • 让问题本身消失
      • 圆珠笔笔芯漏油解决
  • 圆珠笔笔芯在写2W次就开始漏油,如果要解决这个问题本身,那么就是加入更好的材料,更高端的技术,如果是让问题本身消失呢,就是固定一个次数,让它只能写1.5W次就没油开始丢弃,这样的两种办法
  • 首选是让问题本身消失,次选是解决这个问题本身
  • 方案一:从业务场景消除分布式事物
    • 思路:核心业务先处理,其他业务异步处理
  • 方案二:柔性分布式事物

柔性分布式事物实践

  • 通用处理思路
    • 本地事物-->短事物
    • 分布式事物-->长事物
    • 转变成多个短事物
    • 案例
      • A[下单]->B[减库存]->C[支付]
        • A->DB1
        • B->DB2
        • C->DB3
        • A/B/C都成功
        • A/B成功,C失败
          • 补偿
  • 业务场景
    • 异步场景
      • 基于MQ消息驱动分布事物
    • 同步场景
      • 基于异步补偿分布

异步场景分布式事物设计

异步场景

商品交易

下单,支付

方案一:业务方提供本地操作成功回查功能

    • 事物消息:MQ提供类似X/Open XA的分布式事物功能,通过MQ事物消息能达到分布式事物的最终一致
    • 半消息:暂不投递的消息,发送方已将消息成功发送到了MQ服务端,但是服务端未收到生产者对该消息的二次确认,此时该消息被标记成"暂不能投递"状态,处于该种状态下的消息即半消息
    • 消息回查:由于网络闪断,生产者应用重启等原因,导致某条事物消息的二次确认丢失,MQ服务端通过扫描发现某条消息长期处于半消息时,主要主动向消息生产者询问该消息的最终状态(Commit或Rollback),即消息回查
  • MQ分布式事物设计方案

  • MQ分布式事物消息设计
    • MQ事物消息设计事物消息作为一种异步确保型事物,将两个事物分支通过MQ进行异步解耦,MQ事物消息的设计流程同样借鉴了两阶段提交理论,整体交互流程如上图
    1. 事物发起方首先发送prepare消息到MQ
    2. 在发送prepare消息成功后执行本地事物
    3. 根据本地事物执行结果返回commit或rollback
    4. 如果消息是rollback,MQ将删除该prepare消息,不进行下发,如果是commit消息,MQ会将消息发送给consumer端
    5. 如果执行本地事物过程中,执行端挂掉,或者超时,,MQ服务器端将不停的询问producer来获取事物状态
    6. consumer端的消费成功机制有MQ保证
  • 成本:
    • MQ需要支持半消息
    • MQ需要提供消息遍历
    • 业务方需要提供回查接口
  • 业务方接入步骤

  • 优点
    • 通用
  • 缺点
    • 业务方需要提供回查接口,对业务侵入大
    • 发送消息非幂等
    • 消费端需要处理幂等

方案二:本地事物消息表

  • 本地操作和发送消息通过本地事物强一致性
    • 本地事物操作表
    • 本地事物消息表
      • mqMessages(msgid,content,topic,status)

  • 发送端消息不幂等
    • At least once (最少发一次)
    • Once Only (只发一次)
    • At more once(最多发一次)
  • 消费端处理消息幂等
    • 分布式锁
  • A->B->C
    • A/B成功,C失败
      • 记录错误日志
      • 报警
      • 人工介入
  • 优点
    • 业务入侵小

相比于提供消息回查接口(RockectMQ)来说,实际异步场景还是本地消息事物表使用的比较多

同步场景分布式事物设计

  • 同步场景
    • 首页推荐商品列表
      • 商品信息
      • 用户信息
      • 社交信息
    • 购买商品
      • 下单->A
      • 减库存->B
      • 支付->C

通过业务逻辑层驱动

  • 解决方案
    • 基于异步补偿的分布式事物
    • 架构设计的三大关键点

开始记录调用请求的参数,如果失败后基于参数做补偿接口,接口需要保证幂等性

  • 总体架构设计

场景:A下单,B减库存,C支付,在调用接口的时候,A先走Proxy存入事物ID,状态,参数等信息,然后执行本地事物,接着B,C走同样的流程如果都成功,那么事物状态改成2,也就是成功,如果在C失败的时候可以更具参数,事物ID对A,B进行补偿

业务逻辑层Proxy设计(基于AOP实现)

    • 逻辑层调用上加上事物注解@Around("execution(**(..)) && @annotation(TX)")
    • Proxy在真正业务逻辑被调用之前,生成一个全局唯一TXID标示事务组,TXID保存在ThreadLocal变量中,方法开始前写入,完成后清除,并向远端数据库写入TXID并把事务组制成开始状态
    • 业务逻辑层调用数据访问层之前,通过RPCProxy代理记录,当前调用请求参数
    • 如果业务正常,调用完成后,当前方法的调用记录删除或者存档
    • 如果业务异常,查询调用链反向补偿

  • 数据访问层设计
    • 原子接口
    • 补偿接口
      • 谁来提供?
        • 业务方提供
      • 幂等性保证
        • 采用本地资源锁,锁定唯一资源
    • 基于原则接口方法,在方法名加注解标注补偿方法名
    • @Compensable(cancelMethod = "cancelRecord")

  • 分布式事物补偿服务
    • 事物组表(数据库表TDB)    
      • 记录事物组状态
      • txid state timestamp
    • 事物调用组表(数据库表TDB)
      • 记录事物组内的每一次调用,以及相关参数
      • txid actionid callmethod pramatype params
    • 补偿策略
      • 调用执行失败,修改事物组状态
      • 分布式事物补偿服务异步执行补偿

分布式事物成功案例

  • 二手交易创建订单事务组正常流程
    • 锁库存->减红包->创建订单
  • 代理层透明记录调用请求参数
    • 记录事物域的开始与结束
    • 在所有远程调用成功时
    • 对业务逻辑不做侵入

分布式事物失败案例

  • 二手交易创建订单事务组异常流程
    • 微服务数据访问层失败,代理更改事务组状态
    • 微服务业务正常执行
    • 事物补偿服务异步执行补偿

好了,到这里分布式事物也就写完了..休息一下,,哎,又到了找工作的时候了,有需要可以联系我

03-27 08:15