分布式事务

学习孙玄老师的公开课,企业级分布式事务,笔记。

使用场景

一次请求涉及数据分布多个存储系统:

  1. 多个DB;
  2. DB和Redis;
  3. DB和MQ;

业务场景容忍度:

  1. 购买商品,商品、订单、支付;不能失败,必须用分布式事务。
    –金融场景,要么都成功,要么都失败
    –要求最终一致性

  2. 朋友圈发布信息,发布信息,朋友圈小红点;允许失败,对用户伤害不大,用分布式事务,那不是给自己找困难吗?
    – 社交场景
    – BASE理论即可满足(CAP理论不太适合,最终还是要数据一致性)

分布式事务的本质

以购买商品为例:

  1. 同步场景;
    – 减库存,建立订单
    – 前台支付

  2. 异步场景;
    – 前台超时未支付,普适的操作,使用MQ延迟消息(创建订单的时候,发一个延迟消息,查看订单状态)

基本上所有分布式事务场景都能满足AP,主要是CP。

分布式事务普适方法论(DB MQ Redis ES)

方法论:

  1. 拆分;A -> DB, B -> MQ, C -> ES, D -> Cache
    – 分布式事务,也叫长事务
    – 本地事务,也叫短事务

方法论就是把长事务拆分成短事务,把上面的ABCD拆分开来,每个都成功了,分布式事务就成功了,一个失败就都失败了。

那失败了怎么办?补偿

  1. 补偿,人为规定一个补偿步骤
    – AB成功,C失败
    – 补偿AB,不用补偿C

分布式事务普适方法论的业务场景

– AB成功,C失败
– 补偿AB,不用补偿C

  1. 场景1 多个DB
    – C是本地事务,直接就rollback了;AB的补偿要注意幂等

  2. 场景2 DB和Redis
    – Redis setNX 在指定的key不存在时,为key设置指定的值
    – SETNX Key Value 设置成功返回1,失败返回0
    – EXPIRE Key seconds 设置生存时间
    – MULTI 执行多个命令,EXEC,实现如下:

    Redis本身就支持事务,使用LUA实现:

if 
redis.calll(
'setnx',KEYS[1],ARGV[1] )== 1
then
redis.call(
'expire',KEYS[1],ARGV[2]
) 
return 1 else return 0
end

这个lua脚本如何使用java调用:

在这里插入代码片
  1. 场景3 DB和RocketMQ,异步的情况,先放DB,再放延迟MQ

  2. 场景4 DB和ES
    – ES不提供本地事务,AP CP模型使用分布式事务,ES不提供分布式事务,ES retry几次仍然失败就扔了,或者记录log,再重试。
    –也就是选择了ES,就满足不了分布式事务,最多记录一下,看一下失败的概率。

分布式事务设计

异步场景 前台超时未支付

场景描述:
下单记录DB,发送MQ到延时消息。
保证事务的方法1:×
1)改成一个本地事务:把保存DB和发送MQ放在一个事务里,try catch,如果发送消息失败,回滚DB操作。
有漏洞!:如果发送消息是Timeout,实际发送消息成功,此时回滚DB操作,就完蛋了。

方法2: ×
2) 调整时序:先发消息,在写DB。
有漏洞!:写DB失败了,但是消息已经发出去了,也完蛋了。

那怎么办?两阶段提交!
过程:1)应用程序发起事务,commit请求。2)事务协调者发起prepare投票。3)事务参与者都同意后,事务协调者再发起commit。4)commit过程出现异常,服务重启后,再次进行commit。
在这里插入图片描述
核心思路:协商!一旦协商成功后,就没有回头路了。不断try,直到成功。

如何设计和落地
1)2PC落地实践
在这里插入图片描述
需要业务系统提供下单成功的回查接口,MQ要支持prepare消息和超时机制回查业务系统。整体设计太过复杂。在工业体系很难落地。不优雅。

2)2PC落地实践优化(消息表)认知,格局,思维模型
第一阶段,消息落地到DB
第二阶段,消息发送MQ,读取消息表中信息发出去,失败了一直重试,直到成功为止。

第二阶段落地关键点
1)发送端消息无法保证幂等,只能保证至少发送一次
2)订单业务逻辑冗余部署,如果他们都去读取未读消息来发送,很有可能会发重复。正常情况下,每台服务器只处理请求自己的请求。会有一种情况发送重复,就是服务重启后,未处理消息同时被多台机器获取(解决方式:加锁)。
3)业务线程不要阻塞,发消息失败就放入到时间轮转动线程(重试线程),要打印日志。
4)补偿线程(MsgTask)失败也无所谓,因为失败了就不会改DB状态,不改状态,还会再次重试。(其中一台机器处理就可以,方式:加锁)
5)清理线程,定时清理已发送的消息,疑问:发送或补偿成功,就删除不好吗?(此时只标记,不删除)
6)持锁线程,工具类,强锁,给补偿线程和清理线程用。
在这里插入图片描述
读操作不需要去重,但写操作一定去重。
异步场景分布式事务不存在性能瓶颈
吞吐量不会有瓶颈,因为DB写入量大,可以拆库;读库量大,可以多加从库,线性扩展;业务逻辑更是可以横向扩展;MQ可以拆分多个topic。每一个环节都是可扩展的
响应延迟不会有瓶颈,同步路径短,毫秒级别,用户能接受,异步处理环节和用户无关。
大前提,分布式事务,一定满足CAP模型,要么CP,要么AP,这种场景一般量都不大,所以不会有性能瓶颈。
锁不会影响性能,锁的粒度可以小一些,就是拆分,锁也可以有集群。但不会无线扩展,ROI和性能的平衡。

同步场景 SAGAS

tcc 对业务侵入太大
Seata 对业务的打包,不算严格的框架
在这里插入图片描述
关键点

  1. 异步补偿,TM(分布式事务补偿服务)
  2. 补偿api
  3. T1->Tn 记录请求参数,写在TDB里

拦截设计
1)在事务开始时,生成事务唯一标识,事务id
2)事务中,每个步骤执行时,都要去TDB中记录自己的入参

Java代码
1)在Java中以动态代理的形式实现,invoke() 前后增加事务

补偿设计
1)事务组表 txId status ts
2)事务调用组表 txId actionId调用步骤顺序 callMethod补偿方法 ptype params补偿参数
3)补偿策略,事务执行失败,修改事务组状态,分布式事务异步调用补偿服务

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值