【MySQL】事务实现原理

事务

事务是将一组SQL语句打包成一个整体,在这组SQL的执行过程中,要么全部成功,要么全部失败。这组SQL语句可以是一条也可以是多条

如果转账成功,应该满足以下要求:

张三的账户余额减少100,变成900;李四的账户余额增加了100,变成1100。不允许出现张三的余额减少而李四的余额没有增加的情况。(原子性)

转账前张三和李四的总余额不变,即转账前他们的余额总数为2000,转账后他们的余额总数也应为2000。(一致性)

转账后的余额结果应当保存到存储介质中,以便日后查询和确认。(持久性)

在转账过程中,张三和李四的余额不能因为其他转账事件而受到干扰。(隔离性)


如何使用

在MySQL中,只有InnoDB引擎支持事务。通过下面的一些语句可以完成对事务的控制。

START TRANSACTION 或 BEGIN:开始一个新的事务。
COMMIT:提交当前事务,并将更改持久化保存到数据库中。
ROLLBACK:回滚当前事务,取消其所有更改,将数据库恢复到事务开始前的状态。


SET autocommit:禁用或启用当前会话的默认自动提交模式。autocommit 是一个系统变量,可以通过选项指定或者通过命令行设置 --autocommit={OFF|ON}。

默认情况下,MySQL启用事务的自动提交模式。这意味着每个单独的SQL语句都被视为一个独立的事务,并且在语句执行完成后会自动提交事务,就好像被 START TRANSACTION 和 COMMIT 包裹一样。因此,不能使用 ROLLBACK 来撤销单个语句的执行结果。如果在语句执行期间发生错误,MySQL会自动回滚当前语句所引起的任何更改。


ACID

ACID(原子性、一致性、隔离性、持久性)模型是数据库设计的重要原则,旨在确保事务处理的可靠性和数据完整性。MySQL的InnoDB存储引擎严格遵循ACID模型。

原子性(Atomicity)

事务中的所有操作要么全部完成并提交,要么全部取消(回滚)。在MySQL中,使用 COMMIT 提交事务或 ROLLBACK 回滚事务来保证原子性。

原子性实现原理

实现回滚的依据就是靠 uodo 日志来完成的。http://t.csdnimg.cn/s7E9X 具体看uodo日志部分。


持久性(Durability)

在提交事务时,MySQL会将数据持久化到存储介质,比如磁盘。通常情况下这一过程是可靠的,但在服务器崩溃或突然断电的情况下,可能会发生事务只部分写入数据文件的情况,导致数据不完整,从而破坏数据的一致性。

持久性实现原理

具体查看Redo日志部分http://t.csdnimg.cn/O5ImZ

为了解决这个问题,MySQL采用了以下几种机制来确保事务的持久化和数据的一致性:

重做日志(Redo Log):在真正将数据写入数据文件之前,MySQL会将事务中的所有数据修改操作记录到重做日志中。这些日志记录允许在服务器重启后重新执行尚未完成的事务操作,从而确保所有必须持久化的数据都能被写入存储介质中。

双写缓冲区(Doublewrite Buffer):MySQL还使用双写缓冲区来避免因写入过程中崩溃而导致的数据文件损坏。它首先将数据写入双写缓冲区,然后再将数据写入实际的数据文件。这种方式可以保证即使在写入过程中出现故障,数据文件也不会受到破坏。

二进制日志(Binary Log):除了重做日志,MySQL还使用二进制日志来记录数据库的所有修改操作。这些日志不仅用于恢复操作,还可以用于数据库复制和恢复等操作。


隔离性

MySQL服务可以同时被多个客户端访问,每个客户端执行的 DML 语句以事务为基本单位。当不同的客户端对同一张表中的同一条数据进行修改时,可能会出现相互影响的情况。为了保证不同的事务在执行过程中不受影响,事务之间需要相互隔离,这种特性就是隔离性。

隔离级别

MySQL通过实现不同级别的事务隔离性(如读未提交、读已提交、可重复读和串行化)来满足不同的应用场景需求,开发人员可以根据具体需求选择合适的隔离级别来平衡并发性能和数据一致性。

读未提交

读未提交的事务:允许事务读取其他事务还未提交的数据(脏读)。

对于name=张三这一行数据,事务A是读操作,事务B是写操作。刚开始事务A读到name=张三,但是事务B还没有提交,它后续把name改成李四。这样,事务A读到的就是脏数据。

读已提交

读已提交的事务:事务只能读到其他事务已提交的数据。事务A使用相同的查询得到不同结果,因为可能其他事务修改了数据并进行了提交。

对于name=张三这一行数据,事务A是读操作,事务B是写操作。刚开始事务A读到name=张三,然后事务B修改name=李四并提交了事务。但是事务A还没有结束,它最后还要读一下这行的值,在查询就是name=李四。两次查询到的结果不一致。

可重复读

可以重复读某一行数据:确保在同一个事务中多次读取相同行的结果是一致的。(InnoDB默认的隔离界别)

对于name=张三这一行数据及其附近行的已有的数据,事务A是读操作,事务B是写操作。刚开始事务A读到了这些行的数据,事务B想修改这些已有行中的一些数据,但这是不允许的,但是事务B可以往这些行中添加一行,这样按照之前的条件查询可能会多出来一行。

串行化

这是最高的隔离级别,它通过强制事务串行执行来完全隔离,从而避免了脏读、不可重复读和幻读。这种级别可以提供最严格的隔离,但可能导致效率低下,因为它限制了多个事务同时操作数据的能力。


隔离级别原理

对于上面的隔离级别,InnoDB使用锁和MVCC来控制不同级别的实现。

从锁粒度上:InnoDB存储引擎的锁粒度可以分为行级锁和表级锁。(MySQL8版本没有页级锁)

从锁模式上(如何申请锁):共享锁(S)、独占锁(X)、意向共享锁(IS)、意向独占锁(IX)、记录所、间隙锁、Next-Key锁(临键锁)、Auto-INC锁(自增锁)等。

共享锁&独占锁

这两个锁是行级锁

共享锁(S锁):允许持有该锁的事务读取表中的一行记录,同时允许其他事务在锁定行上加另一个共享锁并读取被锁定的对象,但不能对其进行写操作

独占锁(X锁):允许持有该锁的事务对数据行进行更新或删除,同时不论其他事务对锁定行进行读取或修改都不允许对锁定行进行加锁

  • 如果事务T1持有R行上的共享锁(S),那么事务T2请求R行上的锁时会有如下处理:

    • T2请求S锁会立即被授予,此时T1和T2都对R行持有S锁。
    • T2请求X锁不能立即被授予,会被阻塞直到T1释放对R行的共享锁。
  • 如果事务T1持有R行上的独占锁(X),那么T2请求R行上的任意类型锁都不能立即被授予,事务T2必须等待事务T1释放R行上的锁。

-- 对查询结果集中的每行数据都加共享锁
SELECT * FROM account WHERE id < 2 FOR SHARE; -- MySQL 8.0及更高版本推荐写法
SELECT * FROM account WHERE id < 2 LOCK IN SHARE MODE; -- MySQL 8.0及之前版本写法

-- 对查询结果集中的每行数据都加排他锁
SELECT * FROM account WHERE id = 1 FOR UPDATE;

-- 可以使用以下SQL在监视器中查看锁信息
SHOW ENGINE INNODB STATUS\G;
意向锁
  • InnoDB支持多粒度锁,允许行锁和表锁共存。
  • InnoDB使用意向锁实现多粒度级别的锁。意向锁是表级别的锁,不是真正的锁,而是记录事务将要对表中的哪些行加哪种类型的锁(共享锁或排他锁)。意向锁分为两种:
    • 意向共享锁(IS):表明事务打算对表中的单个行设置共享锁。
    • 意向排他锁(IX):表明事务打算对表中的单个行设置排他锁。
  • 在获取意向锁时有如下协议:
    • 在事务获得表中某一行的共享锁(S)之前,必须首先获得该表上的IS锁或更强的锁。
    • 在事务获得表中某一行的排他锁(X)之前,必须首先获得该表上的IX锁。
  • 意向锁可以提高加锁的性能,因为在真正加锁之前,不需要遍历表中的行来检查是否已经加锁,只需要查看表中的意向锁即可。
  • 在请求锁的过程中,如果将要请求的锁与现有锁兼容,则将锁授予请求的事务;如果与现有锁冲突,则不会授予,并且事务将阻塞等待,直到冲突的锁被释放。
  • 意向锁与行级锁兼容性如下:
索引记录锁

也叫精准行锁。在索引记录上的一行加锁。

间隙锁

间隙锁锁定的是索引记录之间的间隙,或者第一个索引记录之前,再或者最后一个索引记录之后的间隙。

比如锁(10,20),不包括10和20

间隙可以跨越单个或多个索引值

如果 id 没有被索引或者是一个非唯一的索引,上述语句将锁定对应记录前面的间隙。
不同事务的间隙锁可以共存,一个事务的间隙锁不会阻止另一个事务在相同的间隙上使用间隙锁。
共享间隙锁和独占间隙锁之间没有区别。
当事务隔离级别设置为 READ COMMITTED 时,间隙锁会被禁用,对于搜索和索引扫描不再使用间隙锁定。

临键锁

是指索引记录锁和索引记录之前的间隙上的间隙锁的组合。

插入意向锁

插入意向锁是一种特殊的间隙锁,在向索引记录之前的间隙进行插入操作时使用。如果多个事务尝试向相同索引间隙的不同位置插入记录,则它们不需要互相等待。举例来说,假设已经存在索引值为10和20的记录,两个事务分别尝试插入索引值为15和16的行。在获取插入行上的排他锁之前,每个事务都会用插入意向锁锁定10到20之间的间隙,但它们不会相互阻塞,因为它们所操作的行并不冲突。

自增锁

AUTO-INC锁,也称为自增锁,是一种表级锁,用于服务配置了 AUTO_INCREMENT 自增列的表。在插入数据时,会在表上加上自增锁,并生成自增值,同时阻塞其他事务的操作,以确保值的唯一性。需要注意的是,当一个事务执行新增操作并生成了自增值,但事务回滚时,申请到的主键值不会回退,这意味着在表中可能出现自增值不连续的情况。


 MVCC

频繁加锁与释放锁对性能影响较大。为了提高性能,InnoDB引入了另一种事务隔离性的实现机制MVCC(Multi-Version Concurrency Control,多版本并发控制)。MVCC能够解决脏读、不可重复读等事务间读写问题。在某些场景中,MVCC取代了低效的锁机制在保证隔离性的同时,提升了读取效率和并发性能。

实现原理

MVCC的实现基于Undo Log版本链和ReadView来完成。在执行Update或Delete操作时,每次操作的上一个版本会被记录在Undo Log中。每条Undo Log记录都包含一个称为roll_pointer的引用信息,通过roll_pointer可以将某条数据对应的Undo Log组织成一个Undo链。数据行的头部通过数据行中的roll_pointer与Undo Log中的第一条日志进行关联,从而形成一条完整的数据版本链。

在MVCC中,每条被修改的记录都会形成一条版本链,记录了该数据的所有变更历史。当有事务对这条数据进行修改时,会将修改后的数据连接到版本链的头部。

在MVCC中,确定在查询时要选择哪个版本的数据,需要使用ReadView结构。ReadView是一个内存结构,用于在事务执行SELECT查询时构造一个视图。这个视图中记录了版本链的一些统计值,以便在后续查询处理中不需要遍历所有版本链。这些统计值具体包括:

  • m_ids:当前所有活跃事务的集合(启动还未提交的事务)
  • m_low_limit_id:活跃事务集合中最小事务Id
  • m_up_limit_id:下一个将被分配的事务Id,即版本链头的事务Id + 1
  • m_creator_trx_id:创建当前ReadView的事务Id

这些值帮助系统确定在当前事务开始时,哪些版本是可见的(即哪些版本的数据可以被读取),从而有效地支持MVCC的并发控制和数据一致性。

在执行 SELECT 查询时,当事务 ID 为 201 的事务创建了一个 ReadView 后,根据以下的查询规则,会找到唯一的可用版本:

  • m_ids:活跃事务集合为 [90, 100, 200]
  • m_up_limit_id:活跃事务中最小的事务ID是 90
  • m_low_limit_id:预分配的事务ID为 202,最大事务ID为预分配事务ID减一,即 201
  • m_creator_trx_id:当前创建 ReadView 的事务ID为 201

查找规则

第一步:判断该版本是否为当前事务创建。若 m_creator_trx_id 等于该版本事务ID,则意味着读取自己修改的数据,可以直接访问;如果不等,则进入第二步。

第二步:若该版本事务ID小于 m_up_limit_id(最小事务ID),意味着该版本在生成当前 ReadView 之前已经提交,可以直接访问;如果不是,则进入第三步。

第三步:若该版本事务ID大于等于 m_low_limit_id(最大事务ID),意味着该版本在生成当前 ReadView 之后才创建,所以肯定不能被当前事务访问,因此无需进行第四步判断,直接遍历下一个版本;如果不是,则进入第四步。

第四步:若该版本事务ID在 m_up_limit_id(最小事务ID)和 m_low_limit_id(最大事务ID)之间,同时该版本不在活跃事务列表中,意味着在创建当前 ReadView 时该版本已经提交,可以直接访问;如果不是,则继续遍历并判断下一个版本。

首先,幻读无法单独通过MVCC解决。

  • 对于不可重复读问题,在事务中的第一个查询时创建一个ReadView。后续的查询都使用这个ReadView来判断,因此每次查询的结果都是相同的,从而解决了不可重复读问题。在REPEATABLE READ(可重复读)隔离级别下,采用的就是这种方式。

  • 如果事务每次查询都创建一个新的ReadView,那么就会出现不可重复读问题。在READ COMMITTED(读已提交)隔离级别下,就是采用这种实现方式。

一致性(Consistency)

保证了原子性、持久性和隔离性后,一致性也就自然而然的保证了。

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

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

相关文章

代码随想录第37天|动态规划

01背包理论基础 参考 01背包: 每个物品只有一个, 只要选或不选两个选项 暴力解法: 回溯法枚举 dp[i][j]: i 表示 0 ~ i 的物品, j 表示容量, 数值表示当前的最大价值递推公式: max(dp[i-1][j], dp[i-1][j-weight[i]] value[i])初始化: j 0 时, 无法放任何有价值的物品, d…

Python从0到100(三十三):xpath和lxml类库

1. 为什么要学习xpath和lxml lxml是一款高性能的 Python HTML/XML 解析器&#xff0c;我们可以利用XPath&#xff0c;来快速的定位特定元素以及获取节点信息 2. 什么是xpath XPath&#xff0c;全称为XML Path Language&#xff0c;是一种用于在XML文档中进行导航和数据提取的…

相机网线RJ45连接器双端带线5米8芯绿色网线注塑成型

相机网线RJ45连接器双端带线5米8芯绿色网线注塑成型&#xff0c;这款网线采用了环保的绿色材质&#xff0c;线长5米&#xff0c;足够满足大多数拍摄场景的需求。更重要的是&#xff0c;它采用了8芯设计&#xff0c;保证了数据传输的稳定性和高速性。在接口方面&#xff0c;它采…

JAVA:常用的算法指南

请关注微信公众号&#xff1a;拾荒的小海螺 博客地址&#xff1a;http://lsk-ww.cn/ 1、简述 在软件开发过程中&#xff0c;算法扮演着关键的角色。它们用于解决各种问题&#xff0c;从数据处理到搜索、排序等。本文将介绍几种常见的算法及其 Java 实现&#xff0c;包括排序算…

云计算【第一阶段(24)】Linux文件系统与日志分析

一、文件与存储系统的inode与block 1.1、硬盘存储 最小存储单位&#xff1a;扇区(sector) 每个扇区大小&#xff1a;512字节 1.2、文件存取 最小存取单位&#xff1a;块(block)连续八个扇区组成&#xff1a;块(block) 每个块大小&#xff1a;4K文件数据&#xff1a;实际数据…

“论云上自动化运维及其应用”写作框架,软考高级论文,系统架构设计师论文

论文真题 云上自动化运维是传统IT运维和DevOps的延伸&#xff0c;通过云原生架构实现运维的再进化。云上自动化运维可以有效帮助企业降低IT运维成本&#xff0c;提升系统的灵活度&#xff0c;以及系统的交付速度&#xff0c;增强系统的可靠性&#xff0c;构建更加安全、可信、…

电脑怎么录屏幕视频带声音?2种方法教会你

在数字时代的浪潮中&#xff0c;电脑屏幕视频录制已经成为一项潮流且实用的技能。无论是为了创作短视频、分享游戏过程&#xff0c;还是为了记录在线会议或教程&#xff0c;电脑录屏都是非常重要的功能。但是不少的人都会遇上录制好的视频没有声音的困境&#xff0c;面对这种情…

matrix-breakout-2-morpheus靶场

1 信息收集 1.1 主机发现 arp-scan -l 1.2 端口与服务扫描 发现开放22、80、81端口 2 访问服务 2.1 访问80端口 查看源代码 2.2 访问81端口 3 目录扫描 3.1 dirsearch目录扫描 dirsearch -u 192.168.1.14 发现robots.txt文件和javascript文件 访问文件 http://192.168…

HarmonyOS Next开发学习手册——显示图片 (Image)

开发者经常需要在应用中显示一些图片&#xff0c;例如&#xff1a;按钮中的icon、网络图片、本地图片等。在应用中显示图片需要使用Image组件实现&#xff0c;Image支持多种图片格式&#xff0c;包括png、jpg、bmp、svg和gif&#xff0c;具体用法请参考 Image 组件。 Image通过…

CS-隐藏防朔源-数据转发-iptables(Linux自带的防火墙)

免责声明:本文仅做技术交流与学习... 目录 准备环境: 1-iptables转发机设置转发: 2-CS服务器配置iptables服务器的IP 准备环境: 两台外网服务器. --iptables服务器就是做一个中转...封了中转就没了... 1-iptables转发机设置转发: iptables -I INPUT -p tcp -m tcp --dport 8…

【K8s】专题六(3):Kubernetes 稳定性之自动扩缩容

以下内容均来自个人笔记并重新梳理&#xff0c;如有错误欢迎指正&#xff01;如果对您有帮助&#xff0c;烦请点赞、关注、转发&#xff01;欢迎扫码关注个人公众号&#xff01; 一、基本介绍 在 Kubernetes 中&#xff0c;自动扩缩容是一种动态调整集群资源&#xff0c;以灵活…

Linux基础篇-文件句柄数修改

目录 文件句柄数修改修改最大限制用户级的修改系统级的修改 文件句柄数修改 linux最大打开文件句柄数&#xff0c;即打开文件数最大限制&#xff0c;就是规定的单个进程能够打开的最大文件句柄数量&#xff08;Socket连接也算在里面&#xff0c;默认大小1024&#xff09; liu…

Fluent在Linux环境导入文本文件报错解决

问题描述 同样的文本文件&#xff08;如Profile文件、Chemkin反应机理文件等&#xff09;&#xff0c;Fluent在Windows环境中可正常读取和运算&#xff0c;但是在Linux环境中导入会出错。 Linux中&#xff0c;Fluent读取Chemkin文件报错 问题原因 可能原因之一&#xff1a;换…

交叉编译 tcpdump libpcap

文章目录 交叉编译 tcpdump & libpcap概述源码下载交叉编译 libpcap交叉编译 tcpdump 交叉编译 tcpdump & libpcap 概述 tcpdump 是一个强大的命令行包分析器&#xff0c;libpcap 是一个可移植的用于网络流量捕获的 C/C 库。tcpdump 依赖于 libpcap 库&#xff0c;同…

基于IMX8MPlus SMARC核心板的便携式床旁超声诊断仪应用解决方案

医学的高速发展&#xff0c;使得超声仪器得到了广泛的普及&#xff0c;便携式的床旁超声诊断仪&#xff0c;不仅满足临床医学对可视化、便携式、智能化的需求&#xff0c;还能满足基层患者随时随地快速筛查的需求。 便携式的床旁超声诊断仪&#xff0c;移动灵活方便&#xff0c…

检索增强生成RAG系列3--RAG优化之文档处理

在上一章中罗列了对RAG准确度的几个重要关键点&#xff0c;主要包括2方面&#xff0c;这一章就针对其中一方面&#xff0c;来做详细的讲解以及其解决方案。 目录 1 文档解析1.1 文档解析工具1.2 实战经验1.3 代码演示 2 文档分块2.1 分块算法2.2 实战经验2.3 代码演示 3 文档e…

OmniViD: A Generative Framework for Universal Video Understanding

标题&#xff1a;OmniViD:通用视频理解的生成框架 源文链接&#xff1a;https://openaccess.thecvf.com/content/CVPR2024/papers/Wang_OmniViD_A_Generative_Framework_for_Universal_Video_Understanding_CVPR_2024_paper.pdfhttps://openaccess.thecvf.com/content/CVPR202…

HarmonyOS Next开发学习手册——文本输入 (TextInput/TextArea)

TextInput、TextArea是输入框组件&#xff0c;通常用于响应用户的输入操作&#xff0c;比如评论区的输入、聊天框的输入、表格的输入等&#xff0c;也可以结合其它组件构建功能页面&#xff0c;例如登录注册页面。具体用法请参考 TextInput 、 TextArea 。 创建输入框 TextIn…

优化数据库字段使用位运算-php语言示例

背景&#xff1a;一个会员有三个状态&#xff0c;A、B、C&#xff0c;其中一个人可以为 A、B、C、AB&#xff1b;之前数据表结构加了三个字段is_a、is_b、is_c; 本人实在不想这样粗糙的实现需求&#xff0c;遂决定用位运算优化。 上代码&#xff1a; 位运算可以用来处理状态值…

注塑机压铸机比例泵PQ阀放大器

比例泵技术在注塑机和压铸机中的应用&#xff0c;主要通过调节液压泵的排量和压力&#xff0c;来实现对设备工作性能的优化和节能。比例泵技术的核心在于其能够根据实际需求&#xff0c;精确地控制液压系统的压力和流量。这一点对于注塑机和压铸机来说尤为重要&#xff0c;因为…