可落地的、不基于框架的分布式事务解决方案

news2024/11/27 22:30:09

两阶段提交 2PC

在MySQL InnoDB中,为了保证Bin Log和Redo Log的一致性,便采用了两阶段提交;ZooKeeper、ETCD集群为了保证数据一致性,也采用了两阶段提交,RocketMQ的事务消息也采用了两阶段提交,可见两阶段提交是分布式事务中比较常用的解决方案。

MySQL InnoDB中的两阶段提交

我们看下在MySQL InnoDB是如何采用两阶段提交是保证Bin Log和Redo Log的一致性的:

  1. 收到客户端的数据操作请求后,MySQL InnoDB会在内存中完成数据更新;
  2. 写Redo Log,此时Redo Log的状态为Prepare;
  3. 写Bin Log,此时Redo Log的状态不变,还是为Prepare;
  4. 提交事务,此时Redo Log的状态为Commit;

由于本篇博客,重点不在于MySQL,所以就不介绍为什么需要Bin Log和Redo Log两种日志了,只需要知道只有这样才可以保证数据的一致性,而且可以看出Redo Log有两种状态:Prepare、Commit,这两种状态便是两阶段提交的核心。有了这个基础,我们就可以来看看两阶段提交的概念了。

两阶段提交 2PC

两阶段提交把一个操作分为了Prepare、Commit/Rollback两个阶段:

  1. 事务协调器向多个本地资源管理器发起Prepare请求;
  2. 本地资源管理器收到事务协调器的Prepare请求,会执行自身的操作,但是不会真正生效,随后会响应事务协调器;
  3. 如果事务协调器在一定时间内,收到所有本地资源管理器的Prepare响应,会向本地资源管理器发起Commit请求,如果事务协调器在一定时间内,没有收到所有本地资源管理器的Prepare响应,会向本地资源管理器发起Rollback请求;
  4. 如果本地资源管理器收到的是事务协调器的Commit请求,会提交自身的操作,使其真正生效;如果本地资源管理器收到的是事务协调器的Rollback请求,会回滚自身的操作;
  5. 不管本地资源管理器执行的是Commit操作,还是Rollback操作,都会响应事务协调器。

事务协调器也被称之为“TC”,本地资源管理器也被称之为“RM”。

关于两阶段提交,网上有很多说法都不太一样,因为这仅仅是一个思想,是一个理论,不同的人有不同的理解,真正做起来,也会有不同的实现,比如【本地资源管理器收到事务协调器的Prepare请求,会执行自身的操作】,【本地资源管理器收到事务协调器的Commit请求后,会提交自身的操作】这两个操作:

  • 本地资源管理器收到事务协调器的Prepare请求,可以是开启一个本地事务,执行自身的业务逻辑,但是不提交事务,当本地资源管理器收到事务协调器的Commit请求后,才会提交事务;当本地资源管理器收到事务协调器的Rollback请求后,会回滚事务。这种是传统的两阶段提交实现方式,事务可能比较长,也会长时间占用数据库资源,性能比较低下,但是可靠性比较高。
  • 本地资源管理器收到事务协调器的Prepare请求,可以将业务逻辑的反操作记录下来(相当于Undo Log),随后释放数据库资源,当资源管理器收到事务协调器的Commit请求后,这条反操作记录就无效了(因为操作成功了,不会利用这反操作记录进行数据的恢复了);当本地资源管理器收到事务协调器的Rollback请求,可以利用反操作记录来进行数据的回滚。如果是这种实现,不会长时间占用数据库资源,性能可能会有所提高,可靠性会相对差一些(这就是Seata AT模式的实现)。

虽说两阶段提交有不同的实现,但是整体上一定分为Prepare、Commit/Rollback这两个阶段。

传统的两阶段提交存在下以下几个问题:

  1. 性能低下:传统的两阶段提交会长时间占用数据库资源,直到Commit/Rollback才释放;
  2. 事务协调器故障:如果事务协调器挂了,怎么办?特别是在第二阶段,本地资源管理器在等待事务协调器的Commit/Rollback请求,此时事务协调器挂了,那本地资源管理器就会处于“悬而未决”的“懵逼”状态;
  3. 某个本地资源管理器Commit/Rollback失败,怎么办?
  4. 使用场景限制:一般来说,传统的两阶段提交是基于事务的,所以需要数据库支持事务,如果业务逻辑中有对Redis、Mongo、ES的操作,传统的两阶段提交就不太适用了。

讲道理,在介绍完两阶段提交后,应该要介绍三阶段提交了,但是两阶段提交存在的问题,在三阶段提交中,并没有改善多少(也有可能是我没有领悟出来),所以就不介绍三阶段提交了。

TCC

TCC是支付宝提出的,是两阶段提交的变种,TCC是三个单词的缩写:Try、Confirm、Cancel。Confirm对应两阶段提交中的Commit,Cancel对应两阶段提交中的Rollback。

如图所示:

  1. 调用方会向所有服务提供方发起Try请求,服务提供方根据业务要求,做一系列操作:比如检查数据、锁定资源等,然后会响应调用方;
  2. 如果在一定时间内,调用方收到了所有服务提供方的Try响应,会向所有服务提供方发起Confirm请求,服务提供方收到Confirm请求后,执行自身的业务逻辑。

但是“天佑不测风云,人有祸福旦夕”,不可能总是一帆风顺,如果在一定时间内,调用方没有收到服务提供方的Try响应或者收到了服务提供方的“Try-No”响应,调用方会向所有服务提供方发起Cancel请求:

以前我一直不太明白,如果在一定时间内,调用方没有收到服务提供方的Try响应或者收到了服务提供方的“Try-No”响应,为什么调用方还要向所有服务提供方发起Cancel请求,直接不管不就可以了,反正服务提供方还没有真正执行业务逻辑?因为在Try阶段,服务提供方可能做了锁定资源的操作,所以需要通知服务提供方,让其释放资源。

TCC在一定程度上解决2PC的缺陷:

  1. 如果某个服务提供方Confirm、Cancel失败了,通过TCC框架可以不停的重试,直到成功;
  2. TCC并非是基于数据库事务的,所以没有使用场景的限制;
  3. TCC并非是基于数据库事务的,不会长时间占用数据库资源。

那TCC是不是完美的解决方案呢?也不是,TCC在一定程度上解决2PC的缺陷的同时,又新增了缺陷:

  1. 服务提供方需要提供Try、Confirm、Cancel三个接口,代码侵入性比较强,比较繁琐;
  2. 由于没有数据库事务的概念,所以难以保证数据的一致性。

不管是采用2PC,还是TCC,几乎都会引入框架,一旦引入框架,复杂度、学习成本、维护成本就成倍上升了,那有没有不需要框架,就可以解决分布式事务问题的方案呢?

基于事件状态表的检查+事后补偿

调用方依赖多个服务,如何做到一致性呢?可以新建一张事件状态表,核心字段如下:

  • state:事务状态(执行中、执行成功、执行失败)
  • content:事务内容
  1. 调用方先往事件状态表插入一条数据,state为“执行中”;
  2. 调用方调用各个接口;
  3. 如果调用一切正常,万事大吉,修改state为“执行成功”;
  4. 如果其中一个接口出现异常,修改state为“执行失败”;
  5. 通过定时任务的方式,扫描事件状态表,拿到需要重试(state为“执行中”/“执行失败”)的事务,调用接口,进行重试,重试成功,修改state为“执行成功”,重试失败,state不变。

你可能会问,如果调用接口正常,修改state失败,怎么办?大不了定时任务重试一次,但是需要事先和服务提供方沟通好,保证幂等性。

后台对账

  1. 异步消息对账,这个方案在可落地的分布式事务解决方案这篇博客中,已经介绍过了;
  2. 全量对账:定时任务对比多个库的全量数据;
  3. 增量对账:基于更新时间,对比多个库的增量数据。

还有一种比较“隐性”的场景,比如订单从“支付完成”到“商家确认”,一般不超过24小时,那就意味着,如果超过了24小时,可能就触发了异常场景,定时任务可以针对这种数据进行处理。

前台对账

举个例子,一般来说电商网站,热门商品的库存数据在数据库中有一份,在Redis也有一份,为了性能,商品详情中展示的库存,可能是Redis中的,到了扣减库存环节,既要扣除数据库中的库存,又要扣除Redis中的库存,数据库中的库存才是最可靠的,所以在执行扣减库存的操作的时候,会对比Redis中的库存和数据库中的库存,如果不一致,就用数据库中的库存覆盖Redis中的库存(当然也可以不管三十二十一,直接用数据库中的库存福覆盖Redis中的库存)。这种方案比较适合轻量级的业务操作,比如查库存、修改库存,都是很快、很轻的操作。

妥协方案:基于业务特性的弱一致性+基于事件状态表的检查+事后补偿

  • TCC是一个同步的方案,有两个阶段,性能比较差;
  • 基于事件状态表的检查+事后补偿,写事件状态表是一个同步的操作,而且需要两次操作事件状态表,性能比较差,事后补偿是一个事后的操作,及时性比较差;
  • 后台对账是一个事后的操作,及时性比较差。

如果既要让系统之间最大限度的保证一致性,性能又不能太受影响,应该怎么办?

举个创建订单的例子,创建订单分为两步:创建订单、扣减库存。

一般来说,电商是不允许超卖发生的,因为超卖导致发不了货,会影响平台信誉,用户体验,平台可能还需要进行赔偿,所以宁愿多扣减库存,也不能少扣减库存,我们就可以利用这个特性,来设计我们的弱一致性方案:先扣减库存,再创建订单:

  • 扣减库存成功,创建订单成功,没有任何问题;
  • 扣减库存成功,创建订单失败,多扣减了库存;
  • 扣减库存失败,不再创建订单。

但是库存多扣减了,怎么进行补偿呢?

一般来说,库存扣减会生成库存扣减记录,库存扣减记录有状态、时间两个字段,扣减库存后,库存扣减记录的状态为“占用中”,支付完成后,库存扣减记录的状态为“释放”。定时任务可以扫描长时间处于“占用中”的库存扣减记录,进行库存的回收,同时还可以取消订单。

妥协方案:重试+报警+人工补偿

调用方调用接口失败,重试一定的次数,如果最终还是失败的,就发送一条通知,人工干预。虽然这种方案很“丑陋”,但是非常实用,毕竟调用接口失败,是非常少见的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/88433.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从VirtualBox换成KVM虚拟机管理程序?

好消息是,您可以轻松地将VDI格式的VirtualBox VM迁移到qcow2(即KVM的磁盘映像格式),不用创建新的KVM来宾计算机。 我们在本文中将概述如何将VirtualBox VM迁移到Linux中KVM VM的逐步过程。 第一步:列出现有的VirtualBox映像 首先&#xff0c…

泰斯公式Thiem’s equation地下水

基本形式 泰斯公式1描述了在含水层抽水时的地下水流动。 多井作业时非承压含水层的方程形式如下 H(s)和H0(s)分别表示s点的估计地下水位和初始地下水位,K表示水力导率,ri表示预测位置与贡献井i之间的距离,n是贡献井的集合,Q表…

Win11 21H2 12月最新更新了哪些内容?

微软今天发布了12月最新的累积更新补丁,用户可以升级KB5021234将版本号提升至 build 22000.1335,并解决了远程网络问题以及可能影响数据保护应用程序编程接口 (DPAPI) 解密的问题。此外,该更新还包括之前在 11 月 15 日…

11-FreeRTOS配置函数 FreeRTOSConfig.h

1-FreeRTOSConfig.h介绍 FreeRTOS中的相关定义多数都在FreeRTOSConfig.h中,整个FreeRTOS的定义调用都可以在这里定义,当然你也可以自己命名一个文件实现自定义。 下面是这个文件的内容,如下: #ifndef FREERTOS_CONFIG_H #define…

Graph Neural Networks for Social Recommendation学习笔记

1 目标 学习user embedding和item embedding。 2 框架 3 用户建模 3.1 利用历史记录对用户建模 3.2 利用社交关系对用户建模

10.9.1-Dataway+Echarts动态图表方案

文章目录1. 技术选型2. 实现方案2.1. 方案介绍2.2. 方案实现(demo)2.2.1. 使用echarts绘制html静态页2.2.1.1. 选择合适的图表2.2.1.2. 下载html demo2.2.2. 使用Dataway准备数据接口2.2.2.1. 部署dataway2.2.2.2. 创建数据接口2.2.3. 调试html demo da…

代码随想录算法训练营第七天| 哈希表理论基础 ,454.四数相加II, 383. 赎金信, 15. 三数之和, 18. 四数之和

代码随想录算法训练营第七天| 哈希表理论基础 ,454.四数相加II, 383. 赎金信, 15. 三数之和, 18. 四数之和 454.四数相加II 建议:本题是 使用map 巧妙解决的问题,好好体会一下 哈希法 如何提高程序执行效…

【洞察人性】 理解行为背后的动机

《洞察人性》 关于作者 阿尔弗雷德•阿德勒,奥地利精神病学家, 人本主义心理学先驱,曾经在美国哥伦比亚大学任客座教授。同时他也是个体心理学的创始人,在学术界拥有重要的地位。著作有《自卑与超越》《人性的研究》 《洞察人性…

DPDK源码分析之rte_eal_init

EAL是什么 环境抽象层(EAL)负责获得对底层资源(如硬件和内存空间)的访问。对于应用程序和其他库来说,使用这个通用接口可以不用关系具体操作系统的环境细节。rte_eal_init初始化例程负责决定如何分配操作系统的这些资源(即内存空间、设备、定时器、控制台等等)。 …

【IVIF:搜索架构】

Searching a Hierarchically Aggregated Fusion Architecture for Fast Multi-Modality Image Fusion (搜索用于快速多模态图像融合的分层聚合融合架构) 现有的基于CNN的方法使主要点在于设计各种体系结构,以端到端的方式实现这些任务。但是…

JSP ssh美食娱乐分享网站系统myeclipse开发oracle数据库MVC模式java编程计算机网页设计

一、源码特点 JSP ssh美食娱乐分享网站系统是一套完善的web设计系统(系统采用ssh框架进行设计开发),对理解JSP java编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采 用B/S模式开发。开发环境为TOMCA…

学习二叉树必须要了解的各种遍历方式及节点统计

哈喽,大家好,我是小林。今天给大家分享一下对二叉树的一些常规操作。 愿我们都能保持一颗向上的心。 目录一、前序遍历二、中序遍历三、后序遍历四、 统计节点个数五、统计叶子节点个数六、第K层的节点个数七、二叉树的深度八、查找值为x的节点九、层序遍…

TensorFlow TFRecords简介

TensorFlow TFRecords简介 这篇博客将介绍TensorFlow的TFRecords,提供有关TFRecords的所有信息的一应俱全的介绍。从如何构建基本TFRecords到用于训练 SRGAN 和 ESRGAN 模型的高级TFRecords的所有内容。包括什么是TFRecords,如何序列化,反序…

SQL 语句练习03

目录 一、建表 二、插入数据 三、查询 一、建表 这里先建好我们下面查询需要的表,方便后续查询。 建立如下学生表(命名格式“姓名拼音_三位学号_week5s”, 如LBJ_023_week5s)create table LYL_116_week5s(SNO varchar(4) primary key,SNA…

【Kubernetes】DashBoard部署

kubernetes,是一个全新的基于容器技术的分布式架构领先方案,是谷歌严格保密十几年的秘密武器----Borg系统的一个开源版本,于2014年9月发布第一个版本,2015年7月发布第一个正式版本。 kubernetes的本质是一组服务器集群&#xff0…

数字孪生智慧水务建设综述

随着新时期治水方针的逐步落实,水利现代化、智能化建设已全面开启,数字孪生等新技术的成熟,也为智慧水务体系的搭建提供了技术保障,新时代治水新思路正逐步得到落实。本文将重点对智慧水务的内涵及建设内容进行解读,力…

2022年航空与物流行业研究报告

第一章 行业概况 航空与物流行业是指以各种航空飞行器为运输工具,以空中运输的方式运载人员或货物的企业。航空公司是以各种航空飞行器为运输工具为乘客和货物提供民用航空服务的企业。航空公司使用的飞行器可以是他们自己拥有的,也可以是租来的&#x…

物联网通信技术原理-作业汇总(更新ing)

第一章 第二章 第三章 第四章 第五章 1. 移动通信中典型的多址接入方式有哪些?简要说明其工作原理2. 分集技术的基本原理是什么?简要说明空间、频率和时间分集、合并的异同。 1)分集技术的基本原理 通过多个信道(时间、频率或…

25.访客功能

访客功能 一、需求分析 用户在浏览我的主页时,需要记录访客数据,访客在一天内每个用户只记录一次。 首页展示最新5条访客记录 我的模块,分页展示所有的访客记录 二、数据库表 visitors(访客记录表) { “_id”: …

尚医通 (三十五) --------- 预约下单

目录一、预约下单前端1. 封装 api 请求2. 页面修改二、后端逻辑1. 需求分析2. 搭建 service-order 模块3. 添加订单基础类4. 封装 Feign 调用获取就诊人接口5. 封装 Feign 调用获取排班下单信息接口6. 实现下单接口7. 预约成功后处理逻辑① rabbit-util 模块封装② 封装短信接口…