最全的分布式事务详情,它来啰~

我们首先得理解什么是分布式事务呢?分布式事务是指在分布式系统中,涉及多个计算机或服务器的操作序列,这些操作需要满足一致性和可靠性的要求。每个操作要么全部成功执行,要么全部回滚,以保持数据的一致性和完整性。

OK,概念讲完,我们知道一个东西的出现一定是为了解决一些问题的,那么分布式事务解决了我们什么样的难题呢,而它又是如何解决的呢?

  1. 数据一致性问题:当数据分布在不同的节点上,多个并发操作可能会导致数据不一致。分布式事务通过提供原子性和一致性,确保在分布式系统中进行的操作保持数据一致性,避免数据错误和不一致。
  2. 并发冲突问题:在分布式环境中,多个并发操作可能会相互干扰,导致数据竞争和冲突。分布式事务的隔离性能力确保并发操作之间相互隔离,防止数据冲突,确保操作的正确执行。
  3. 故障容忍和持久性问题:分布式系统中的节点可能面临故障或断电等问题。分布式事务通过持久性的特性,确保一旦事务成功提交,其结果将被永久保存,即使在系统故障或网络中断的情况下也不会丢失。

所以说,事务通过原子性、一致性、隔离性、持久性这几大特点,保障数据一致性和完整。性防止并发操作导致的数据竞争和冲突 提供故障容忍和持久性,确保操作的安全性和可靠性

如果你还对这些东西感到晕头转向,那么让我给你一个简单的场景吧,让你对它有更加深刻的理解

当你在网上购物时,使用信用卡进行支付涉及多个步骤和参与者,包括你、商家、支付网关和银行系统。在这个过程中,分布式事务的功能起到以下作用:

  1. 原子性:如果交易成功,整个支付过程要么全部成功执行,要么全部回滚。例如,如果商家没有库存或者支付网关出现问题,整个支付过程将被取消,避免了部分支付导致的数据不一致性。
  2. 一致性:分布式事务确保支付过程中的各个环节符合预定义的一致性规则。例如,商家应该从库存中减少商品数量,支付网关应该扣除相应的金额,并向银行发起付款请求。这种一致性保证了交易的正确执行,并避免了数据错误或矛盾的发生。
  3. 隔离性:分布式事务确保你的支付操作与其他用户的支付操作相互隔离,不会相互干扰。这意味着你的支付过程不会与其他用户的支付过程产生冲突,每个支付操作都独立执行,保证了数据的正确性和独立性。
  4. 持久性:一旦支付成功提交,分布式事务确保支付结果被永久保存。无论在支付过程中发生了什么,一旦支付成功,金额将从你的账户扣除并转入商家的账户。这种持久性保证了支付数据的安全存储,即使在网络故障或系统崩溃的情况下也能够恢复数据并保持一致性。

你看!一个我们日常中最常见的购物操作,都会涉及到事务的处理,所以话不多说,赶紧来了解一下事务到底是什么吧!!

事务的隔离级别

隔离级别是指在并发环境下,数据库系统为了处理事务之间可能发生的相互干扰问题而采取的一种机制。

如果想了解事务的隔离级别这个概念,让我们先来问几个问题,这些问题可能你们在生产中也会遇到

1、你是否遇到过在一个事务中读取到了另一个事务尚未提交的数据?比如发送一个消息,后端需要先更新消息明细列表数据,并且在统计表中对用户未读数的统计+1,但查询时,发现明细列表有数据了,但未读数怎么没+1呢

2、你是否曾经在同一个事务中多次读取同一数据时得到了不一致的结果?

3、你是否遇到过在同一个事务中执行了一次范围查询,然后再次执行相同查询时结果却不一样?

上面这几个问题就代表着我们可能遇到;脏读、不可重复读、幻读

那么什么又是脏读、不可重复读、幻读呢?

  • 脏读(Dirty Read):某个事务读取了另一个事务尚未提交的数据。这可能导致读取到不正确的数据,因为尚未提交的数据可能会被回滚,从而造成脏读。
  • 不可重复读(Non-repeatable Read):在同一个事务中,某个数据多次读取的结果不一致。这是由于其他事务在该事务读取数据期间修改了数据,导致不一致的读取结果。
  • 幻读(Phantom Read):在同一个事务中,某个范围查询多次执行的结果不一致。这是由于其他事务在该事务范围查询期间插入、删除或更新了符合查询条件的数据,导致出现新增或消失的数据行。

了解这些问题,我们再来看下事务隔离是如何解决这些问题的:

  1. 读未提交(Read Uncommitted):最低级别的隔离级别,允许事务读取尚未提交的数据。它无法解决脏读、不可重复读和幻读问题。
  2. 读已提交(Read Committed):在事务提交后才允许其他事务读取数据。它解决了脏读问题,但仍可能遇到不可重复读和幻读问题。
  3. 可重复读(Repeatable Read):事务执行期间保持一致的快照视图,其他事务无法修改已读取的数据。它解决了脏读和不可重复读问题,但仍可能遇到幻读问题。
  4. 串行化(Serializable):最高级别的隔离级别,确保事务串行执行,避免了脏读、不可重复读和幻读问题。它通过对事务加锁来实现串行化执行,但可能影响系统的并发性能。

每个隔离级别在提供数据一致性和隔离性方面都有不同的权衡和性能影响。根据应用程序的需求和对数据一致性的要求,我们可以选择合适的隔离级别来平衡并发性能和数据的正确性。

读已提交

最基本的事务隔离级别是读已提交(Read Committed),它提供了两个保证:

  1. 从数据库读时,只能看到已提交的数据(没有脏读(dirty reads))。
  2. 写入数据库时,只会覆盖已经写入的数据(没有脏写(dirty writes))。

没有脏读

在读已提交隔离级别运行的事务必须防止脏读,就是一个事物只能读取另一提交的事务。这意味着事务的任何写入操作只有在该事务提交时才能被其他人看到。

为什么要防止脏读呢?

  • 如果事务需要更新多个对象,脏读取意味着另一个事务可能会只看到一部分更新。就像我前文中举的例子,用户看到了未读数的差异
  • 如果数据库允许脏读,那就意味着一个事务可能会看到稍后需要回滚的数据,即从未实际提交给数据库的数据。 想想后果就让人头大。

没有脏写

如果先前的写入是尚未提交事务的一部分,又会发生什么情况,后面的写入会覆盖一个尚未提交的值?这被称作脏写。

举一个简单的例子,Alice和Bob两个人同时试图购买同一辆车。购买汽车需要两次数据库写入:网站上的商品列表需要更新,以反映买家的购买,销售发票需要发送给买家。在 图7-5的情况下,销售是属于Bob的(因为他成功更新了商品列表),但发票却寄送给了 Alice(因为她成功更新了发票表)。读已提交会阻止这样这样的问题。
在这里插入图片描述
如何实现读已提交呢?

最常见的情况是,数据库通过使用行锁(row-level lock)来防止脏写:当事务想要修改特定对象(行或文档)时,它必须首先获得该对象的锁。然后必须持有该锁直到事务被提交或中止。

但用锁的问题也很明显,可能会因为长事务导致其他事务等待,导致响应时间增加。

出于以上的原因,所以有了一种新的方案:对于写入的每个对象,数据库都会记住旧的已提交值,和由当前持有写入锁的事务设置的新值。 当事务正在进行时,任何其 他读取对象的事务都会拿到旧值。 只有当新值提交后,事务才会切换到读取新值。(类比于CAS无锁化)

我们如何用代码实现呢?

  1. 显式设置隔离级别:可以在数据库连接或事务开始时显式设置隔离级别为读已提交。具体的方法取决于你使用的数据库系统和编程语言。比如,在SQL Server中,可以使用下面的语句设置隔离级别为读已提交:
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

在Java中,可以使用以下代码设置隔离级别为读已提交:

Connection connection = DriverManager.getConnection(url, username, password);
connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);

快照隔离和可重复读

然而读已提交并不是完美的,我们来看另外一种场景
在这里插入图片描述
Alice在银行有1000美元的储蓄,分为两个账户,每个500美元。现在一笔事务从她的一个账户中转移了100美元到另一个账户。如果她在事务处理的同时查看其账户余额列表,不幸地在 转账事务完成前看到收款账户余额(余额为500美元),而在转账完成后看到另一个转出账户 (已经转出100美元,余额400美元)。对Alice来说,现在她的账户似乎只有900美元—— 看起来100美元已经消失了。

当然,你可能会说只需要等一小会,重新刷新一下,就会好了,但对于用户来说,这种场景还是蛮惊悚的

要解决这个问题,我们可以想到,让Alice在查询数据时,保持“数据静止”不就行了,这就是我们快照隔离的解决方案:

每个事务都从数据库的一致快照(consistent snapshot)中读取——也就是说,事务可以看到事务开始 时在数据库中提交的所有数据。即使这些数据随后被另一个事务更改,每个事务也只能看到 该特定时间点的旧数据。

那么,我们改如何去实现快照隔离呢?

从性能的角度来看,快照隔离的一个关键原则是:读不阻塞写,写不阻塞读。数据库在处理一致性快照上的长时间查询时,可以正常地同时处理写入操作。且两者间没有任何锁定争用。

我们的答案是多版本并发控制(MVCC, multi-version concurrentcy control)

我们用一个简单的比喻来描述什么是快照隔离,来更好理解MVCC:

假设你和你的朋友共享一个电子相册,其中包含了你们的旅行照片。每当你上传新的照片时,系统会为该照片创建一个时间戳,以便记录照片的版本。现在,假设你的朋友在某个时间点查看了你们的相册,并记住了这个时间点。然后你添加了一张新照片到相册中。

在MVCC机制下,当你的朋友再次查看相册时,他只能看到在他最初查看相册时已经存在的照片,而看不到你后来添加的照片。这是因为他的时间点早于新照片的时间戳,所以他只能看到旧版本的相册。

通过这个例子,我们可以更好地理解MVCC机制的工作原理。每个事务(每个人查看相册)使用自己的时间点(时间戳)来选择合适的数据版本(相册的内容),以保证数据的一致性和隔离性。这样,即使其他事务(其他人)对数据进行了修改,只要它们的时间点晚于当前事务开始的时间点,当前事务仍然可以读取到旧版本的数据,从而实现了可重复读。

持快照隔离的存储引擎通常也使用MVCC来实现读已提交隔离级别。很简单,已提交隔离级别保留一个对象的两个版本就足够了:提交的版本和被覆盖但尚未提交的版本。所以说MVCC是一种通用的实现方案
在这里插入图片描述
我们来看一下,用MVCC机制实现时,什么时候对象对于我们是可见的呢?

  • 读事务开始时,创建该对象的事务已经提交。
  • 对象未被标记为删除,或如果被标记为删除,请求删除的事务在读事务开始时尚未提交。

接下来还有一个问题:我们实现的数据的隔离,但是别忘了我们还有索引,数据的改变也会引起索引的变动,那么索引是如何在多版本数据库中工作的?

我们思考一下,有以下几种思路

1、索引简单地指向对象的所有版本,并且需要索引查询来过滤掉当前事务不可见的任何对象版本。当垃圾收集删除任何事务不再可见的旧 对象版本时,相应的索引条目也可以被删除。这种实现方式想想就工作量巨大,事务的状态变更将同时要修改索引,这些索引甚至大概率不在一个地方存储,带来的性能损耗可想而知

2、有些数据库(比如CouchDB,Datomic和LMDB),它们使用的是一种仅追加/写时拷贝(append-only/copy-on-write)的B树,它们在更新时不覆盖树的页 面,而为每个修改页面创建一份副本。从父页面直到树根都会级联更新,以指向它们子页面 的新版本。任何不受写入影响的页面都不需要被复制,并且保持不变。使用仅追加的B树,每个写入事务(或一批事务)都会创建一颗新的B树,当创建时,从该特定树根生长的树就是数据库的一个一致性快照。没必要根据事务ID过滤掉对象,因为后续写入不能修改现有的B树;它们只能创建新的树根。但这种方法也需要一个负责压缩和垃圾收集 的后台进程。

丢失更新问题

我们现在考虑这样的一种场景:

如果应用从数据库中读取一些值,修改它并写回修改的值(读取-修改-写入序列),则可能会发生丢失更新的问题。如果两个事务同时执行,则其中一个的修改可能会丢失,因为第二个 写入的内容并没有包括第一个事务的修改。

一些简单的场景就是:统计数值的并发更新、两个用户同时编辑wiki页面

上面的这种问题就是丢失更新问题

那么,针对这种问题,我们应该如何解决呢?我们肯定想到只要保证操作具有‘锁’的特性就行

1、原子写。这个要依赖数据库的支持。比如下面mysql的示例,还有MongoDB提供原子操作,redis的一些原子操作命令

UPDATE counters SET value = value + 1 WHERE key = 'foo';

2、显式锁定。就是加锁啰,FOR UPDATE加行锁

BEGIN TRANSACTION;
SELECT * FROM figures
WHERE name = 'robot' AND game_id = 222
FOR UPDATE;

UPDATE figures SET position = 'c4' WHERE id = 1234;
COMMIT;

3、CAS。加锁效率太低,CAS就能提供无锁化操作

UPDATE wiki_pages SET content = '新内容' WHERE id = 1234 AND content = '旧内容';

写入偏差与幻读

幻读是指在一个事务内,多次执行相同的查询操作,但结果却出现了新增或删除的数据行,就好像出现了幻觉一样。这是因为在这期间有其他事务插入、删除或修改了符合查询条件的数据,导致查询结果发生变化。

举一个简单的例子:

假设你去超市购买水果,你想购买的是超市里所有的苹果。你进入超市时,你看到苹果架上有10个苹果。于是你把苹果放进购物车,准备结账。但在你结账的时候,超市员工往苹果架上添加了5个苹果。当你回头看苹果架时,你发现有15个苹果。这就好像发生了幻觉,因为在你的视野中苹果的数量突然增加了。

在数据库中,幻读问题的发生方式类似。当一个事务在执行范围查询时,其他事务在这期间插入了新的符合查询条件的数据,导致了查询结果的变化,就像出现了幻觉一样。

写偏差指的是在一个事务内,多次执行相同的写操作,但结果却出现了不一致的情况。这是因为在这期间有其他事务对相同的数据进行了修改,导致写操作的结果不一致。幻读则是在一个事务内多次执行相同的范围查询操作时,结果出现了新增或删除的数据行。这是因为在这期间有其他事务对符合查询条件的数据进行了插入或删除,导致查询结果的变化。写偏差和幻读之间存在关系,即写偏差可能引发幻读问题。当一个事务在执行写操作时,如果另一个事务在同一时间对相同的数据进行了修改,那么在之后执行相同的范围查询操作时,可能会出现新增或删除的数据行,产生幻读。

快照隔离可以避免只读查询中幻读,但是在像读写事务中,幻影会导致特别棘 手的写歪斜情况。

后面的文章中,我会带你继续了解可序列化相关内容,可以关注一下哦~

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

假期出行小程序+chatgpt旅游攻略

马上五一了,如果想出去旅游,需要提取规划好路线图,我们可以借助chatgpt的路线规划功能帮我们生成一份攻略,按照攻略我们就可以愉快的出去玩耍了。 本文结合chatgpt,利用低代码工具帮我们制作一份旅行导览小程序,可以按照行程方便的出行。 1 制定攻略 我们在聊天窗口输…

从小白到黑客高手:一份全面详细的学习路线指南

前言 黑客从入门到精通需要经过深入的学习和实践,这是一个需要长时间投入和大量精力的过程。在这份学习路线中,我将为你介绍黑客学习的基本知识和技能,帮助你逐步掌握黑客技能。 黑客 一、入门阶段 1.了解计算机基础知识 学习计算机基础知…

thinkPhP6.0安装教程图解--PHP框架安装

ThinkPhP 6.0 安装 1.环境检查 首先,thinkphp6.0,要求php的环境是7.2.5及以上的,所以先检查自己的php环境是否符合要求。 在cmd命令窗口中输入php -v 或者没有配置环境变量的话,可以在php编辑器中输出php_info()或则PHP_VERSIO…

【鲁棒优化、无功优化】两阶段鲁棒优化的主动配电网动态无功优化【IEEE33节点】(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

A股市场上股票行情数据接口有那几种?

L2行情数据接口相比Level-1接口相比,L2行情市场具有数据更完整、推送速度更及时的优势,帮助投资者及时把握盘中主要资金流,做出更准确的投资决策。简而言之,Level-2最大的作用就是提前看到主力的大单,对于追逐日线跌停…

Linux高并发服务器开发01:Linux系统编程入门

GCC 什么是GCC GCC工作流程 GCC常用的参数选项 GCC和G的区别 静态库 什么是库 静态库的制作 先编译生成对应的.o文件 gcc -c add.c sub.c mult.c div.c -I …/include/ 再将.o文件打包成静态库 ar rcs libcalc.a add.o sub.o mult.o div.o 使用的时候-l指定库名称 gcc main.c…

【论文简述】Multi-View Stereo Representation Revisit: Region-Aware MVSNet(CVPR 2023)

一、论文简述 1. 第一作者:Yisu Zhang 2. 发表年份:2023 3. 发表期刊:CVPR 4. 关键词:MVS、3D重建、符号距离场 5. 探索动机:像素深度估计仍存在两个棘手的缺陷。一是无纹理区域的估计置信度较低。二是物体边界附…

RTSP/RTP on TCP 协议抓包记录

仅做记录,无他。 RTSP OPTIONS 客户端发送: 服务端响应 RTSP DESCRIBE 客户端发送 服务端响应 RTSP SETTUP 客户端发送请求 服务端响应 RTSP PLAY 客户端发送请求 服务端响应 RTP包 这个比较复杂,得好好解析&#xff0…

消息队列中间件 - RabbitMQ消息的持久化、确认机制、死信队列

持久化和应答机制Ack 消息队列中间件系列的最后一篇了,RabbitMQ消息的持久化、确认机制、死信队列、负载均衡等一系列进行说明。 消息持久化 当RabbitMq重启以后,未消费的消息,可以在服务重启后继续消费,不会丢失。 应答机制A…

DS215KLDCG1AZZ03A如何编写温度比例的代码?

DS215KLDCG1AZZ03A如何编写温度比例的代码&#xff1f; 可编程逻辑控制&#xff0c;简称PLC&#xff0c;由美国机械工程师迪克莫利于1年1968月<>日首次设计。PLC最初是为了减少汽车行业员工的工作量而开发的&#xff0c;从那时起&#xff0c;它们已被用于所有其他恶劣环境…

MySQL --- 多表查询

多表查询、事物、以及提升查询效率最有手段的索引 一. 多表查询 1.1 多表查询 --- 概述 1.1.1 数据准备 将资料中准备好的多表查询数据准备的SQL脚本导入数据库中。 部门表&#xff1a; 员工表&#xff1a; 1.1.2 介绍 多表查询&#xff1a;指从多张表中查询数据&#…

尚硅谷大数据技术Spark教程-笔记01【Spark(概述、快速上手、运行环境、运行架构)】

视频地址&#xff1a;尚硅谷大数据Spark教程从入门到精通_哔哩哔哩_bilibili 尚硅谷大数据技术Spark教程-笔记01【Spark(概述、快速上手、运行环境、运行架构)】尚硅谷大数据技术Spark教程-笔记02【SparkCore(核心编程、案例实操)】尚硅谷大数据技术Spark教程-笔记03【SparkSQL…

云原生背景下如何配置 JVM 内存

image.png 背景 前段时间业务研发反馈说是他的应用内存使用率很高&#xff0c;导致频繁的重启&#xff0c;让我排查下是怎么回事&#xff1b; 在这之前我也没怎么在意过这个问题&#xff0c;正好这次排查分析的过程做一个记录。 首先我查看了监控面板里的 Pod 监控&#xff1a;…

奶爸式Swagger教学

目录 一、导入依赖 二、SwaggerConfig基础编程 三、Swagger 常用说明注解 1.API 2.ApiOperation 3.ApiModel 4.ApiModelProperty 5.ApiParam 6.ApilmplicitParam 一、导入依赖 <!--开启Swagger --><!-- https://mvnrepository.com/artifact/io.springf…

Vector - 常用CAN工具 - CANoe软件安装常见问题

目录 一、确认电脑系统盘是否满足要求&#xff0c;CANoe软件对PC要求如下&#xff1a; 二、确认软件安装包完整 三、确认软件与电脑系统之间的兼容性 四、关闭后台程序 五、安装软件 六、清空临时文件夹 七、尝试在其他电脑上安装 一、确认电脑系统盘是否满足要求&#…

STM32 调试TM7711驱动原理图驱动代码

本文使用工程代码如下 (1条消息) STM32调试TM7711驱动原理图驱动源代码&#xff0c;参考如下博客&#xff0c;有原理图设计资源-CSDN文库 背景 项目选用TM7711&#xff0c;还是很令人吃惊的&#xff0c;主要是有如下几个理由 第一就是便宜 第二精度高 STM32的ADC精度不够…

微生物常见统计检验方法比较及选择

谷禾健康 微生物组经由二代测序分析得到庞大数据结果&#xff0c;其中包括OTU/ASV表&#xff0c;物种丰度表&#xff0c;alpha多样性、beta多样性指数&#xff0c;代谢功能预测丰度表等&#xff0c;这些数据构成了微生物组的变量&#xff0c;大量数据构成了高纬度数据信息。 针…

我的『1024』创作纪念日

目录 ◐机缘 ◑收获 ◐日常 ◑成就 ◐憧憬 记得&#xff0c;2020年07月22日我撰写了第1篇技术博客&#xff1a;《遗传算法实例解析》在这平凡的一天&#xff0c;我赋予了它不平凡的意义也许是立志成为一名专业T作者、也许是记录一段刚实践的经验但在那一刻&#xff0c;我已…

傅里叶分析的历史背景

目录 1. Fourier级数(三角级数)的历史背景 2. 圆和复平面 3. Fourier的大胆猜想 1. Fourier级数(三角级数)的历史背景 自古以来&#xff0c;圆形一直是(现在仍然是&#xff09;最简单的抽象理解形状。您只需要一个中心点和一个半径就可以了。圆上的所有点与圆心的距离都是固定…

Java Web开发:Spring Boot和Spring Cloud的应用和实践

一、介绍 Java Web开发是当今互联网时代中的一项重要技术&#xff0c;随着互联网的发展和应用场景的不断扩大&#xff0c;Java Web开发技术也得到了广泛的应用。而Spring Boot和Spring Cloud作为Java Web开发中最常用的技术之一&#xff0c;已经成为了很多开发者必备的技能。本…