当前位置: 首页 > 编程日记 > 正文

分布式事务有哪些解决方案?

本文我们来讨论下分布式事务的相关知识点。

分布式事务是分布式系统中非常重要的一部分,最典型的例子是银行转账和扣款,A 和 B 的账户信息在不同的服务器上,A 给 B 转账 100 元,要完成这个操作,需要两个步骤,从 A 的账户上扣款,以及在 B 的账户上增加金额,两个步骤必须全部执行成功;否则如果有一个失败,那么另一个操作也不能执行。

分布式事务的经典应用比如转账扣款,下订单扣库存,新会员送积分等等涉及多个业务共同参与在一个请求中。

那么像这种转账扣款的例子,在业务中如何保证一致性,有哪些解决方案呢?

分布式事务是什么

顾名思义,分布式事务关注的是分布式场景下如何处理事务,是指事务的参与者、支持事务操作的服务器、存储等资源分别位于分布式系统的不同节点之上。

简单来说,分布式事务就是一个业务操作,是由多个细分操作完成的,而这些细分操作又分布在不同的服务器上;事务,就是这些操作要么全部成功执行,要么全部不执行。

数据库事务

数据库事务的特性包括原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durabilily),简称 ACID。

在数据库执行中,多个并发执行的事务如果涉及到同一份数据的读写就容易出现数据不一致的情况,不一致的异常现象有以下几种。

脏读,是指一个事务中访问到了另外一个事务未提交的数据。例如事务 T1 中修改的数据项在尚未提交的情况下被其他事务(T2)读取到,如果 T1 进行回滚操作,则 T2 刚刚读取到的数据实际并不存在。

不可重复读,是指一个事务读取同一条记录 2 次,得到的结果不一致。例如事务 T1 第一次读取数据,接下来 T2 对其中的数据进行了更新或者删除,并且 Commit 成功。这时候 T1 再次读取这些数据,那么会得到 T2 修改后的数据,发现数据已经变更,这样 T1 在一个事务中的两次读取,返回的结果集会不一致。

幻读,是指一个事务读取 2 次,得到的记录条数不一致。例如事务 T1 查询获得一个结果集,T2 插入新的数据,T2 Commit 成功后,T1 再次执行同样的查询,此时得到的结果集记录数不同。

脏读、不可重复读和幻读有以下的包含关系,如果发生了脏读,那么幻读和不可重复读都有可能出现。

不同隔离级别

SQL 标准根据三种不一致的异常现象,将隔离性定义为四个隔离级别(Isolation Level),隔离级别和数据库的性能呈反比,隔离级别越低,数据库性能越高;而隔离级别越高,数据库性能越差,具体如下:


(1)Read uncommitted 读未提交

在该级别下,一个事务对数据修改的过程中,不允许另一个事务对该行数据进行修改,但允许另一个事务对该行数据进行读,不会出现更新丢失,但会出现脏读、不可重复读的情况。

(2)Read committed 读已提交

在该级别下,未提交的写事务不允许其他事务访问该行,不会出现脏读,但是读取数据的事务允许其他事务访问该行数据,因此会出现不可重复读的情况。

(3)Repeatable read 可重复读

在该级别下,在同一个事务内的查询都是和事务开始时刻一致的,保证对同一字段的多次读取结果都相同,除非数据是被本身事务自己所修改,不会出现同一事务读到两次不同数据的情况。因为没有约束其他事务的新增Insert操作,所以 SQL 标准中可重复读级别会出现幻读。

值得一提的是,可重复读是 MySQL InnoDB 引擎的默认隔离级别,但是在 MySQL 额外添加了间隙锁(Gap Lock),可以防止幻读。

(4)Serializable 序列化

该级别要求所有事务都必须串行执行,可以避免各种并发引起的问题,效率也最低。

对不同隔离级别的解释,其实是为了保持数据库事务中的隔离性(Isolation),目标是使并发事务的执行效果与串行一致,隔离级别的提升带来的是并发能力的下降,两者是负相关的关系。

分布式事务产生的原因

分布式事务是伴随着系统拆分出现的,前面我们说过,分布式系统解决了海量数据服务对扩展性的要求,但是增加了架构上的复杂性,在这一点上,分布式事务就是典型的体现。

在实际开发中,分布式事务产生的原因主要来源于存储和服务的拆分。

存储层拆分

存储层拆分,最典型的就是数据库分库分表,一般来说,当单表容量达到千万级,就要考虑数据库拆分,从单一数据库变成多个分库和多个分表。在业务中如果需要进行跨库或者跨表更新,同时要保证数据的一致性,就产生了分布式事务问题。在后面的课程中,也会专门来讲解数据库拆分相关的内容。

服务层拆分

服务层拆分也就是业务的服务化,系统架构的演进是从集中式到分布式,业务功能之间越来越解耦合。

比如电商网站系统,业务初期可能是一个单体工程支撑整套服务,但随着系统规模进一步变大,参考康威定律,大多数公司都会将核心业务抽取出来,以作为独立的服务。商品、订单、库存、账号信息都提供了各自领域的服务,业务逻辑的执行散落在不同的服务器上。

用户如果在某网站上进行一个下单操作,那么会同时依赖订单服务、库存服务、支付扣款服务,这几个操作如果有一个失败,那下单操作也就完不成,这就需要分布式事务来保证了。

分布式事务解决方案

分布式事务的解决方案,典型的有两阶段和三阶段提交协议、 TCC 分段提交,和基于消息队列的最终一致性设计。

2PC 两阶段提交

两阶段提交(2PC,Two-phase Commit Protocol)是非常经典的强一致性、中心化的原子提交协议,在各种事务和一致性的解决方案中,都能看到两阶段提交的应用。

3PC 三阶段提交

三阶段提交协议(3PC,Three-phase_commit_protocol)是在 2PC 之上扩展的提交协议,主要是为了解决两阶段提交协议的阻塞问题,从原来的两个阶段扩展为三个阶段,增加了超时机制。

TCC 分段提交

TCC 是一个分布式事务的处理模型,将事务过程拆分为 Try、Confirm、Cancel 三个步骤,在保证强一致性的同时,最大限度提高系统的可伸缩性与可用性。

两阶段、三阶段以及 TCC 协议在后面的课程中我会详细介绍,接下来介绍几种系统设计中常用的一致性解决方案。

基于消息补偿的最终一致性

异步化在分布式系统设计中随处可见,基于消息队列的最终一致性就是一种异步事务机制,在业务中广泛应用。

在具体实现上,基于消息补偿的一致性主要有本地消息表和第三方可靠消息队列等。

下面介绍一下本地消息表,本地消息表的方案最初是由 ebay 的工程师提出,核心思想是将分布式事务拆分成本地事务进行处理,通过消息日志的方式来异步执行。

本地消息表是一种业务耦合的设计,消息生产方需要额外建一个事务消息表,并记录消息发送状态,消息消费方需要处理这个消息,并完成自己的业务逻辑,另外会有一个异步机制来定期扫描未完成的消息,确保最终一致性。

下面我们用下单减库存业务来简单模拟本地消息表的实现过程:

(1)系统收到下单请求,将订单业务数据存入到订单库中,并且同时存储该订单对应的消息数据,比如购买商品的 ID 和数量,消息数据与订单库为同一库,更新订单和存储消息为一个本地事务,要么都成功,要么都失败。

(2)库存服务通过消息中间件收到库存更新消息,调用库存服务进行业务操作,同时返回业务处理结果。

(3)消息生产方,也就是订单服务收到处理结果后,将本地消息表的数据删除或者设置为已完成。

(4)设置异步任务,定时去扫描本地消息表,发现有未完成的任务则重试,保证最终一致性。

以上就是基于本地消息表一致性的主流程,在具体实践中,还有许多分支情况,比如消息发送失败、下游业务方处理失败等,感兴趣的同学可以思考下。

不要求最终一致性的柔性事务

除了上述几种,还有一种不保证最终一致性的柔性事务,也称为尽最大努力通知,这种方式适合可以接受部分不一致的业务场景。

分布式事务有哪些开源组件

分布式事务开源组件应用比较广泛的是蚂蚁金服开源的 Seata,也就是 Fescar,前身是阿里中间件团队发布的 TXC(Taobao Transaction Constructor)和升级后的 GTS(Global Transaction Service)。

Seata 的设计思想是把一个分布式事务拆分成一个包含了若干分支事务(Branch Transaction)的全局事务(Global Transaction)。分支事务本身就是一个满足 ACID 的 本地事务,全局事务的职责是协调其下管辖的分支事务达成一致,要么一起成功提交,要么一起失败回滚。

在 Seata 中,全局事务对分支事务的协调基于两阶段提交协议,类似数据库中的 XA 规范,XA 规范定义了三个组件来协调分布式事务,分别是 AP 应用程序、TM 事务管理器、RM 资源管理器、CRM 通信资源管理器。关于 XA 规范的详细内容,将会在后面的课时中介绍。

总结

掌握分布式事务是学习分布式系统的必经之路,今天介绍了分布式事务的概念,回顾了数据库事务和不同隔离级别,以及分布式事务产生的原因,最后介绍了分布式事务的几种解决方案。

相关文章:

SpringCloud Alibaba集成 Gateway(自定义负载均衡器)、Nacos(配置中心、注册中心)、Loadbalancer

要为未被某些网关路由谓词处理的请求提供相同的CORS配置,请将属性spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping设置为true。断言(Predicate):Java8中的断言函数,Spring Cloud Gateway中的断言函数输入类型是 Spring5.0框架中的ServerWebExchange。对于所有GET请求的路径,来自docs.spring.io的请求都将允许CORS请求。

zookeeper集群部署以及zookeeper原理

ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。ZooKeeper包含一个简单的原语集,提供Java和C的接口。

Zookeeper概要、协议、应用场景

Zoopkeeper提供了一套很好的分布式集群管理的机制,就是它这种基于层次型的目录树的数据结构并对树中的节点进行有效管理,从而可以设计出多种多样的分布式的数据管理模型,作为分布式系统的沟通调度桥梁。

SSO 单点登录和 OAuth2.0 有何区别?

此方法的缺点是它依赖于浏览器和会话状态,对于分布式或者微服务系统而言,可能需要在服务端做会话共享,但是服务端会话共享效率比较低,这不是一个好的方案。在单点登录的上下文中,OAuth 可以用作一个中介,用户在一个“授权服务器”上登录,并获得一个访问令牌,该令牌可以用于访问其他“资源服务器”上的资源。首先,SSO 主要关注用户在多个应用程序和服务之间的无缝切换和保持登录状态的问题。这种方法通过将登录认证和业务系统分离,使用独立的登录中心,实现了在登录中心登录后,所有相关的业务系统都能免登录访问资源。

一键部署 SpringCloud 微服务,这套流程值得学习一波儿!

一键部署 springcloud 微服务,需要用到 Jenkins K8S Docker等工具。本文使用jenkins部署,流程如下图开发者将代码push到git运维人员通过jenkins部署,自动到git上pull代码通过maven构建代码将maven构建后的jar打包成docker镜像 并 push docker镜像到docker registry通过k8s发起 发布/更新 服务 操作其中 2~5步骤都会在jenkins中进行操作。

Springboot + oauth2 单点登录 - 原理篇

OAuth 协议为用户资源的授权提供了一个安全的、开放而又简易的标准,允许用户授权第三方移动应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方移动应用或分享他们数据的所有内容,OAuth2.0是OAuth协议的延续版本,但不向后兼容OAuth 1.0即完全废止了OAuth1.0。授权码模式(authorization code)密码模式(resource owner password credentials)客户端模式(client credentials) 不常用。

JAVA 中 13 种锁的实现方式

分布式系统时代,线程并发,资源抢占,慢慢变得很重要。那么常见的锁都有哪些?

三种方式实现分布式锁

通过以上过程你可以发现锁的获取是按照创建时间来的,谁先来争取锁谁就先获得锁,因此它实现的是公平锁。答案是不能,以Synchronized关键字为例,Synchronized关键字无论是在偏向锁、轻量级锁还是重量级锁状态都不能实现这点,如重量级锁,重量级锁是靠系统底层的互斥量Mutex实现的,也就是说每个节点(服务器)所使用的互斥量是分开的,节点A的互斥量是无法锁住节点B的线程访问临界区,因此Synchronized关键字只能保证单服务器内的JVM进程的不同线程同步,是不能用做分布式环境中来保证线程同步。

浅谈Java分布式与集群

在日常操作中,相信很多人在怎么理解Java分布式与集群问题上存在疑惑,今天就大概说说,不注意听,觉得两个可能是同一个东西,其实这个是两个概念。一句话概括:分布式是以缩短单个任务的执行时间来提升效率的,而集群则是通过提高单位时间内执行的任务数来提升效率。

java面试题:分布式和微服务的区别

分布式架构解决的是如何将一个大的系统划分为多个业务模块这些业务模块会分别部署到不同的机器上,通过接口进行数据交互的问题。微服务是指很小的服务,可以小到只完成一个功能,这个服务可以单独部署运行,不同服务之间通过rpc调用。分布式架构是将一个大的系统划分为多个业务模块,这些业务模块会分别部署到不同的机器上,通过接口进行数据交互。微服务架构是架构设计方式,是设计层面的东西,一般考虑如何将系统从逻辑上进行拆分,也就是垂直拆分。分布式系统是部署层面的东西,即强调物理层面的组成,即系统的各子系统部署在不同计算机上。

k8s搭建部署(超详细)

Kubernetes是Google 2014年创建管理的,是Google 10多年大规模容器管理技术Borg的开源版本。它是容器集群管理系统,是一个开源的平台,可以实现容器集群的自动化部署、自动扩缩容、维护等功能。快速部署应用快速扩展应用无缝对接新的应用功能节省资源,优化硬件资源的使用可移植: 支持公有云,私有云,混合云,多重云(multi-cloud)可扩展: 模块化, 插件化, 可挂载, 可组合自动化: 自动部署,自动重启,自动复制,自动伸缩/扩展。

什么是数据中台?

说完了数据中台诞生的历史背景,现在,我们应该对数据中台有了一定的了解,那我们现在给数据中台下个定义。自2016年,数据中台被提出以来,不同的人对数据中台有不同的理解,就像一千个读者心中有一千个哈姆雷特,因此也有许多不同的定义,以下是我从一些文章、书籍中搜集到的关于数据中台的定义:数据中台是DT时代的大背景下,为实现数据快(快速)、准(准确)、省(低成本)赋能业务发展的目标,将企业的数据统一整合起来,基于Onedata方法论借助大数据平台完成数据的统一加工处理,对外提供数据服务的一套机制。

弹性搜索引擎Elasticsearch:本地部署与远程访问指南

本文主要讲解如何使用Elasticsearch分布式搜索分析引擎本地部署与远程访问。

分布式系统架构设计之分布式数据存储的备份恢复和监控故障排查

架构师通过设计和实施数据备份和恢复策略,可以最大程度地保障分布式系统在面对数据损失、硬件故障、灾难性事件等情况下的稳定性和可用性。在分布式数据存储中,数据备份和恢复是保障数据存储系统可靠性和容灾性的重要组成部分。通过合理的监控和故障排查策略,可以确保分布式数据存储系统在运行过程中保持高可用性、高性能,并且能够及时应对潜在的故障情况。在分布式系统中,对数据存储进行有效的监控和出现问题后故障排查策略是确保系统稳定性和性能可靠性的关键。

【微服务】springboot整合skywalking使用详解

springboot整合skywalking

在云计算环境中,如何利用 AI 改进云计算系统和数据库系统性能

2023年我想大家讨论最多,热度最大的技术领域就是 AIGC 了,AI绘画的兴起,ChatGPT的火爆,在微软背后推手的 OpenAI 大战 Google几回合后,国内各种的大语言模型产品也随之各家百花齐放,什么文心一言、通义千问、科大讯飞的星火以及华为的盘古等等,一下子国内也涌现出几十种人工智能的大语言模型产品。ChatGPT 爆火之后,你是否有冷静的思考过 AIGC 的兴起对我们有哪些机遇与挑战?我们如何将AI 应用到我们现有的工作学习中?_aigc k8s

光伏发电模式中,分布式和集中式哪种更受欢迎?

5.可实现远距离输送,集中式光伏电站发出的电经高压并网,将电一层层的输送到更高的电压等级,如将高压电输送到华东等地区,以实现西电东输。分布式光伏发电:一般建在楼顶、屋顶、厂房等地方,较多的是基于建筑物表面,就近解决用户的用电问题,通过并网实现供电差额的补偿与外送。1.光伏电源处于用户侧,自发自用,就近发电,就近用电,发电供给当地负荷,视作负载,可以减少对电网供电的依赖,减少线路损耗。4.分布式光伏一般就近并网,线路的损耗很低或者可以说没有,可非常方便的补充当地的电量,供当地及附近的用电用户使用。

Elasticsearch分布式搜索分析引擎本地部署与远程访问

本文主要讲解如何使用Elasticsearch分布式搜索分析引擎本地部署与远程访问

Gitlab基础篇: Gitlab docker 安装部署、Gitlab 设置账号密码

安装docker gitlab前确保docker环境,如果没有搭建docker请查阅“Linux docker 安装文档”可以看到在docker ps -a 打印中看到 容器ID ps 展示的容器ID只时原来的一部分。修改docker镜像的gitlab容器端口前需要把gitlab容器以及docker镜像关闭。通过容器ID就能找到containers下具体哪一个是gitlab容器的配置。修改config.v2.json、hostconfig.json文件。docker 下载 gitlab容器。

docker搭建maven私库Nexus3

阿里代理地址:http://maven.aliyun.com/nexus/content/groups/public/由于nexus的默认端口为8081,我们在启动的时候改为18091后需要修改nexus的配置文件。这样就可以在本地浏览器进入nexus页面了,地址为 服务器ip:18091。右上角登录用户名为admin,密码为之前查看的密码。配置maven-central的代理地址。删除nuget开头的仓库。同时查看admin密码。

Nginx基础篇:Nginx搭建、Nginx反向代理、文件服务器部署配置。

Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Rambler.ru站点(俄文:Рамблер)开发的,公开版本1.19.6发布于2020年12月15日。其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、简单的配置文件和低系统资源的消耗而闻名。2022年01月25日,nginx 1.21.6发布。

什么是分布式锁?Redis实现分布式锁详解

文章浏览阅读151次,点赞4次,收藏3次。在分布式系统中,涉及多个主机访问同一块资源,此时就需要锁来做互斥控制,避免出现类似线程安全问题。而Java中的synchronized只是对当前进程中的线程有效,多个主机实际上是多个进程,那么它就无能为力了,此时就需要分布式锁。

【微服务】mysql + elasticsearch数据双写设计与实现

在很多电商网站中,对商品的搜索要求很高,主要体现在页面快速响应搜索结果。这就对服务端接口响应速度提出了很高的要求。