[微服务感悟] 很好理解的分布式事务

2023-05-16

事务是保证一系列操作是一个整体,要么都执行,要么都不执行。比如A给B转账,A扣钱了,B的账户的钱也要加上去,不能出现A扣钱B不加钱,或者B加钱A不扣钱的情况。在单体程序中,数据库和spring框架已经解决这个这个问题,我只要在需要事务的方法上加上@Translate,或者在Spring配置中某一层甚至全局事务。对于我这种CRUD程序员,最初的2年一直在写代码,居然还不知道事务是什么东西,这说明在单体程序开发中,事务已经被处理的很好了,和我们程序员关系不大,第二也说明不要一直写CRUD的代码,那是在浪费生命。

事务的四大特性是原子性,一致性,隔离型,持久性,简称ACID,上面只有原子性和一致性,其余的可以百度看看。

// springboot配置全局事务
@Bean(name = "txAdviceAdvisor")
public Advisor txAdviceAdvisor() {
  AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
  pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
  return new DefaultPointcutAdvisor(pointcut, txAdvice());
}

在单体服务拆分成多个微服务,一个数据库拆成多个数据库后,原有的事务可能会横跨拆分的多个微服务和其对应的多个数据库的情况。这种横跨多个服务的事务,就是分布式事务。

实际上无论使用什么样的分布式事务,它都会增加程序的复杂度,增加程序的性能消耗,增加程序的不稳定性。对于分布式事务,我们第一考虑是能不能把这个事务放在一个单体服务中,比如微服务拆分不合理导致的分布式事务,解决方案应该是合理的拆分/合并服务,而不是满脑子撞在分布式事务上。

对于分布式事务,最初大家想出的解决方案和单体程序中的事务一样,就是对代码无侵入,程序员感知不到,DB和框架它们自己配合解决,于是提出了两段提交法。

两段提交

两段提交法是事务管理器(Transaction Manager)协调各服务的本地事务来实现分布式事务的一种方式。两段提交存在一个事务管理器服务,业务方将事务提交到事务管理器,事务管理器向所有的资源管理器Resource Manager(数据库)发送预执行操作,并等待所有的资源管理发送执行结果;如果都执行成功,再想所有资源管理器发送commit操作;如果存在失败,则向所有资源管理器发送rollback操作。它依赖于TM和各服务的本地事务。

现在市面上有很多种数据库,mysql,oracle,sqlserver等,也有许多两段提交的事务框架和服务,怎么让不同的数据库都能理解各种分布式事务框架发出的指令呢?这需要大家一个都支持的公认协议,目前大家都支持的公认协议是XA。也就是支持XA的数据库和XA的分布式框架组合就能支持两段式分布式事务。mysql从5.7版本开始支持XA协议。

事务的提交,事务管理器对各个服务的本地事务的协调,本地事务的处理,这些操作对我们这些开发人员都是感知不到的。我们要想使用分布式事务,只要集成某个分布式事务的框架客户端,在程序中配置一下事务管理器地址,在方法上加个注解或者配置全局事务,分布式事务就可以生效并工作了,使用起来和单体程序开发时没有什么不同。

这么容易上手的,基本没有学习成本的分布式事务,大家一定都会选择使用吧?但实际情况并不是如此,基本互联网企业没有使用它的,为什么会这样呢?

先说说微服务的一个思想吧,就是随着服务越来越多,其中某些服务节点发生故障的概率越来越大,做个最坏的估计在服务集群中,每时每刻总会有服务节点出现故障。采取两段提交,最大的隐患是所有服务的事务都由事务管理器来执行,如果事务管理器服务出现问题,就会导致几乎所有服务的事务都会无法执行,整个服务群就崩了,这是它最大的一个隐患。

这里补充一个有趣的项目,针对集群服务器中一定会随机挂掉的想法Netflix公司开发出了一个叫疯狂猴子的项目,它的作用就是随机关掉某些生产环境上的一些服务,Netflix的开发者认为与其害怕服务挂掉,不如拥抱服务挂掉这种情况,把服务降级,熔断处理好一点。Netflix称,自从在生产上上线,他们的服务的抗风险能力强多了。

同时它还存在一个致命的性能问题,从事务管理器向资源管理器(DB)发送预执行到发送commit这个阶段,数据库会把数据给锁住,其他访问该数据的数据库请求会被阻塞排队,当事务管理器发送预执行后挂机了,没有发送commit/rollback指令,资源管理器(DB)会一直锁住那部分数据不让其他数据库连接使用。对于高并发,大量数据读写的情况下,一旦一部分数据被锁住,将会瞬间堆积很多的操作请求,服务一下就可能出现多个连锁超时反应,甚至导致雪崩熔断这种事情。一般数据库层面的锁,都使用version做乐观锁而不是用select from update这种排它锁,也是这个原因。

这个分布式事务的处理方式正是由于有这两大隐患,所以各大互联网公司基本不采用它,而宁愿麻烦一点通过业务代码的方式实现分布式事务。

下面是两段提交分布式事务的流程图
在这里插入图片描述

tcc分布式事务

tcc是一种通过重复补偿来实现分布式事务的做法,在介绍tcc前,先说一个之前实现的简单的,基于重复补偿思路的分布式事务的做法。

那是快递柜的开门取快递并后关门的业务处理:用户关门后,柜机基础服务的将格口状态改为空闲,包裹服务将改包裹状态改为已取出,这里的修改格口状态和修改包裹状态就是一个分布式事务。但是我这里的业务,快递都已经被拿走了,结果已经注定,我需要的就只是数据的最终一致,只需要重复执行直到最终执行成功就可以了,有可能存在数据某一时刻的不一致(格口状态改了,包裹单状态没改),但对整体业务没有影响,保证最终一致性就可以了。

实现是这样的,业务服务异步调用柜机服务的修改格口状态和包裹服务的修改包裹状态,如果返回执行失败,就把任务放入到队列(RabbitMQ)中,业务服务有个线程不断拿mq的任务并执行,修改格口状态和修改包裹状态这两个接口是幂等接口,即如果接口已经执行成功,遇到再次重发的请求就不再执行,直接返回执行成功的结果。

在这里插入图片描述

tcc是Try - Confirm - Cancel的简写,它没有事务管理器这个角色,事务的控制需要程序员自己写在每个发起事务的服务中。这样,每个服务都可以发起,控制事务,而不用都经过一个事务管理器。同时,它也不依赖数据库的本地事务,它每个服务的事务都需要程序员编码实现,在接口层实现try, confirm, cancel的逻辑。这样他就能解决两段式提交中事务都要由事务管理器控制的风险,也能解决本地事务在事务期间内一直使用排它锁锁住数据的问题。但是它极大的增加了程序员的实现业务的难度,因为这一套都需要程序员自己设计并实现。

它的代码大致是这样的

if (try()) {
  commit();
} else {
  cancel();
}

使用这种分布式事务,需要在事务的各个服务都实现try, confirm, cancel三套逻辑接口,业务发起方在事务开始时统一调用各微服务的try接口,这是一个预执行方法,一般这个方法并不会直接修改各业务数据,只是尝试是否能够执行,它是为confirm执行做准备的;如果所有服务的try都执行成功,再调用各服务confirm方法,这时才做业务数据的真正修改;如果try执行过程中发生错误,调用各服务的cancel方法,将数据回滚。try, confirm, cancel的逻辑怎么实现,需要开发者自己去思考怎么实现。

我这里有个tcc事务实现的demo,它模拟的业务是智能仓储柜,OA系统下发入库订单,员工看到订单后将对应的货物放入柜机某一个格口,关门在柜机屏幕上确认放入。后台将订单状态改为完成,柜机库存进行修改,柜机格口状态进行更新。

说说实现,有4个服务,除了eureka是服务中心外,业务服务为订单服务,柜机服务,库存服务。业务由订单服务发起,订单服务会调用库存服务增加库存,柜机服务更新格口状态。订单服务先会调用库存服务和柜机的try接口,如果try执行成功则调用他们commit接口;如果失败调用它们的cancel接口。在try的环节时,会检查订单的状态和该格口的可用库存的数量是否支持这次入柜,并修改订单表的执行状态,库存表的锁定库存和执行状态,通过执行状态字段把数据锁住;在confirm的环节,修改order状态,库存数据,格口状态;在cancel环节,还原数据。

confirmcancel执行失败后,会重复执行直到成功或失败次数到达阀值后记录并转人工处理。为什么这么做,因为try执行成功,说明执行confirm一般一定会成功的;而cancel是数据回滚,也一般是可以成功的,所以这两个方法执行失败了,重复执行有大几率最终执行成功。

github项目地址:https://github.com/programluo/tcc-demo,采用springCloud+h2db技术实现。

这里分享一篇介绍TCC事务具体实现的博客,写的很好 - 终于有人把“TCC分布式事务”实现原理讲明白了!。但博客中例子存在一个小问题,try阶段如果库存服务压根没执行锁定库存+2,就报错了,来到cancel阶段时,执行锁定库存-2,这会导致锁定库存平白的减了2,而实际上这个数应该保持不变。我认为cancel不能通过++--的方式去还原,还要通过不变的数据计算还原。

总结

两段提交的优点是代码无侵入,缺点是存在一个事务管理器,会有事务管理器不可用导致整个服务集群不可用的风险;它的事务依赖db,而db会把数据锁住,直到事务结束,这个也是很大的风险项,会导致一段时间对改数据的请求全部堆积。

tcc更适用于分布式场景,因为在没有一个中心的事务管理器存在,每个事务的管理器都在事务发起业务本身,这样它会避免因事务管理器故障导致整个集群所有服务的事务都无法执行的严重隐患;同时锁定资源采用程序员自己的实现方式,不依赖数据实现,可以避开数据库使用排它锁锁住数据导致堵塞的情况。

tcc分布式事务应该是最流行的分布式事务的处理方式了,目前TCC分布式事务的框架有很多,比如tcc-transaction。不过,现在最火的还是分布式事务的框架还是阿里的Seata,它支持TCC 、XA、 AT、SAGA 事务模式,有阿里背书,这个框架很快就流行了,恐怕以后就是行业标准吧。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[微服务感悟] 很好理解的分布式事务 的相关文章

  • Rust VS JS,Rust 已开始渗入前端~

    本文翻译自 xff1a Rust Is The Future of JavaScript Infrastructure Lee Robinson 1 xff0c 已获得作者授权 xff0c 原文略做修改以符合中文语境 xff08 图片镇楼
  • 查看cookie的3种方式

    1 application中查看 2 network中查看 3 console中通过js查看 4 设置cookie document cookie 61 34 age 61 12 34
  • 关于Hadoop中reducer端combiner的一些思考

    什么是Combiner Functions Many MapReduce jobs are limited by the bandwidth available on the cluster so it pays to minimize t
  • Unity新版ECS框架简介:ECS有什么不同?

    了解过ECS的开发者都知道ECS与Unity原本的开发理念相差很大 xff0c 需要所有Unity开发者重新去学习和适应新的开发框架的代价还是很大的 xff0c Unity为何要做出这么大跨度的尝试呢 xff1f Unity正在尝试解决什么
  • Python 爬取 3 万条游戏评分数据,找到了程序员最爱玩的游戏(附代码)

    本文爬取了游戏网站上所有可见的游戏评分数据进行分析 xff0c 全文包括以下几个部分 xff1a 数据获取数据总览游戏类型分析游戏平台分析游戏名称分析高分游戏汇总代码汇总 全文数据获取及分析均基于python3 6完成 数据获取过程 页面内
  • 手游外挂分类及原理介绍

    一 前言 移动游戏市场近几年突然爆发 xff0c 收入规模快速增长 根据第三方数据统计 xff0c 如图所示 xff0c 国内移动游戏2015年市场规模已达514 6亿 如此火热的市场 xff0c 必然会吸引大量图谋不轨的坏人 外挂已在移动
  • 常见游戏外挂分类及原理概述

    外挂基本概念 要理解外挂 xff0c 首先需要理解网络游戏的数据流 这里所说的数据流定义为游戏本地客户端与游戏后台服务器之间的数据流通 一个数据的产生需要玩家做出对应的操作 xff0c 然后经过网络传输同步到服务器后台 xff0c 服务器后
  • 揭秘《英雄联盟》的游戏数据服务器

    Hey xff0c 大家好 xff01 我是 Bill LtRandolph Clark xff0c 一名英雄联盟的游戏工程师 许多 Rioter 工程师关注大量的内容需要直接发送给玩家问题 这是两个我最近最喜欢的例子之一 xff0c 包括
  • 从纹理中生成法线贴图

    概要 本为主要讲解生成法线贴图的基本方法 xff0c 并在 unity 中进行实现和测试 预备知识 法线贴图和基本的图形学知识 xff0c 基本的向量和极限的知识 高度图或灰度图 一张二维纹理有两个维度 u 和 v xff0c 但其实 xf
  • MySQL死锁产生原因和解决方法

    Mysql 锁类型 一 锁类型介绍 xff1a MySQL有三种锁的级别 xff1a 页级 表级 行级 表级锁 xff1a 开销小 xff0c 加锁快 xff1b 不会出现死锁 xff1b 锁定粒度大 xff0c 发生锁冲突的概率最高 并发
  • Flink 动态实时流计算

    xff08 先给个预告 xff0c 下一期关于Flink的文章会讲如何将机器学习融入Flink中 xff09 摘要 本文提供了一种在流计算中不停机动态加载代码来做到敏捷而快速的开发的思路 代码提供在 Lofka 的 lofka night
  • 通俗说Openvswitch

    Openvswitch xff0c 顾名思义 xff0c Open xff0c 开源的 xff0c v xff0c virtual xff0c 虚拟的 xff0c switch交换机 通俗的讲就是一款开源的软件 xff0c 可以创建虚拟的交
  • 人间还是仙界?聊一聊linux系统的用户空间和内核空间

    我们生活在人间 xff0c 但 西游记 里提到 xff0c 在天上还有一个仙界 人间不知道仙界的存在 xff1b 而仙界知道人间的存在 xff0c 神仙也可以从仙界下凡到人间 xff0c 但是被严格管控的 软件设计的灵感其实都来自于生活 x
  • 什么是实时数据库?

    实时数据库是数据库系统发展的一个分支 xff0c 它适用于处理不断更新的快速变化的数据及具有时间 限制的事务处理 实时数据库技术是实时系统和数据库技术相结合的产物 xff0c 研究人员希望利用数据库 技术来解决实时系统中的数据管理问题 xf
  • 带你阅读linux内核源码:linux内核源代码编程规范

    linux内核代码是许许多多遵循相同内核开发规范的牛人们的共同的创造的结晶 作为一名linux内核或者驱动开发工程师 xff0c 很有必要了解这些内核开发规范 好处有以下几个 xff1a 这些约定或者规范对我们阅读linux内核源码 了解设
  • linux进程上下文、中断上下文介绍,以及为什么软中断不能睡眠?

    linux内核的软中断处理程序中能不能睡眠 xff1f 这是一个值得讨论的问题 答案其实很简单 xff0c 那就是不能 因为Linux的软中断处理程序的运行上下文有可能是中断上下文 xff08 注意此处是有可能 xff0c 而并非一定 xf
  • VS2008用devenv.com命令行工具自动编译工程

    转自 xff1a http www cr173 com html 18500 1 html 在vs2008下面提供了devenv com命令行方式 我们可以从VS安装目录 MicrosoftVisual Studio 9 Common7 I
  • 使用ICMP协议检测网络状态

    ICMP xff08 Internet ControlMessages Protocol xff0c 网间控制报文协议 xff09 是TCP IP协议族的子协议 xff0c 是一种面向无连接的协议 xff0c 在IP和路由器之前传递控制消息
  • c++打印enum class

    span class token keyword enum span span class token keyword class span span class token class name A span span class tok
  • 使用strace查找Emacs启动阻塞的原因(exec-path-from-shell)

    原文地址 https www lujun9972 win blog 2019 09 26 使用strace查找emacs启动阻塞的原因 exec path from shell index html 之前就觉得我的Emacs启动好慢 xff

随机推荐

  • 为Linux安装虚拟PDF打印机

    原文地址 https lujun9972 github io blog 2020 04 11 为linux安装虚拟pdf打印机 index html 今天发现一个 CUPS PDF 项目 可以为 CUPS Common Unix Print
  • ubuntu系统启用shell远程登陆

    Ubuntu desktop系统安装后 xff0c 想使用shell远程登陆 xff0c 会提示 Connecting to 192 168 220 133 22 Could not connect to 39 192 168 220 13
  • 枚举类(ENUM)用法总结

    对于ENUM一直是比较陌生的 xff0c 在和某酷爱ENUM的大神合作时 xff0c 才慢慢接触到ENUM的用法 1 ENUM是什么 xff1f 首先ENUM是一个类 xff0c 不像String int之类的数据结构 xff0c 更类似于
  • Python循环结构练习2

    Problem A xff1a 循环结构 输出数列2 xff0c 5 xff0c 8 xff0c 11 xff0c 14 题目描述 输入正整数n xff08 n 100 xff09 xff0c 输出数列2 xff0c 5 xff0c 8 x
  • KVM网络模型之:PCI Passthrough

    目录 PCI Passthrough技术介绍和KVM中配置 案例 内核启用 重新启动虚拟机实例 PCI Passthrough技术介绍和KVM中配置 PCI Passthrough技术是虚拟化网卡的终极解决方案 xff0c 能够让虚拟机独占
  • 微信开放公众平台,扩展自定义类,定时提醒,定时发消息

    微信开放公众平台 xff0c 扩展自定义类 xff0c 定时提醒 xff0c 定时发消息 lt php class MyapiAction extends BaseAction public function index 微医疗 预约提醒
  • Ubuntu配置iptables规则

    Ubuntu配置防火墙 xff0c 并且开机iptables自启动规则 适用于CentOS 1 登录root账号 span class token comment 切换到root账号 span super 64 super span cla
  • Linux记录用户执行命令

    span class token shebang important bin bash span span class token comment By lumia98 64 vip qq com span span class token
  • Nginx规则配置实例

    配置某个ip或者页面禁止访问及跳转方法 server listen 80 server name www test com cn location proxy redirect off proxy set header host host
  • Debian 11.2安装ssh服务

    切换到root用户 更新软件源 span class token function apt get span update 安装ssh span class token function apt get span span class to
  • Python计算文件大小

    span class token comment usr bin env python span span class token comment coding utf 8 span span class token triple quot
  • Python获取文件内的下一行数据

    span class token comment usr bin env python span span class token comment Version 61 3 8 1 span span class token comment
  • iptables配置实例

    查看当前所有规则 iptables L n 查看所有规则 iptables nL line number 显示行 iptables nvL line number 显示行 清空所有配置 iptables F iptables X iptab
  • 利用Shell脚本校验数据一致性

    span class token shebang important bin bash span span class token comment span span class token comment 检测两台服务器指定目录下的文件一
  • Debian11系统Redis源码安装

    span class token shebang important bin bash span span class token comment Debian11 Redis6 2 6安装 span span class token co
  • iOS 根据文字内容设置cell 的高度

    今天学习一个简单的 根据内容的大小设置cell 的高度 第一步 建两个类 分别继承于UIViewController 和UITableViewCell 第二步 mainViewController h import lt UIKit UIK
  • Python下载GitHub数据

    配置文件 span class token punctuation span Source span class token punctuation span Source path span class token operator 61
  • pip 指定安装位置

    离线安装且指定位置 sudo pip3 install no index find links 61 requirements r requirements txt target 61 usr lib python3 dist packag
  • [微服务感悟] 服务雪崩与熔断器

    文章目录 什么是服务雪崩解决方式熔断器舱壁模式 服务隔离 什么是服务雪崩 之前工作中出现了这样的一个问题 xff0c 有一个业务服务 xff0c 它的功能是政府某部门的文件流转柜 那个业务中原本每个外部请求都有一个独立的线程池去处理任务 x
  • [微服务感悟] 很好理解的分布式事务

    事务是保证一系列操作是一个整体 xff0c 要么都执行 xff0c 要么都不执行 比如A给B转账 xff0c A扣钱了 xff0c B的账户的钱也要加上去 xff0c 不能出现A扣钱B不加钱 xff0c 或者B加钱A不扣钱的情况 在单体程序