【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)

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

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

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

相关文章

Vatee万腾平台:引领行业变革,创新未来

在当今这个快速变化的时代&#xff0c;科技的力量正在以前所未有的速度推动着行业的变革。Vatee万腾平台&#xff0c;以其独特的视角和前瞻性的布局&#xff0c;正引领着行业变革的浪潮&#xff0c;创新着未来的发展方向。 Vatee万腾平台是一家专注于科技研发和创新应用的领军企…

安装PyTorch详细过程(个人过程仅供参考)

1.安装anaconda 2.创建一个虚拟环境 以上步骤默认已经完毕&#xff0c;毕竟只是记录pytorch的安装过程 3.查看个人电脑CUDA版本 winr 输入cmd 回车 输入指令 nvidia-smi 右上角为该电脑所支持的最高CUDA版本 输入命令 nvcc -V 图中即为该电脑所安装的CUDA版本 记住该版…

动捕系统如何解决“超出捕捉范围”的挑战

惯性运动捕捉系统改变了我们捕捉运动的方式&#xff0c;使艺术家、创作者和独立工作室能够摆脱动捕实验室和复杂设置的限制。通过身体上的传感器和无线连接&#xff0c;动捕演员可以自由移动和并作出各种高难度动作。然而具有高自由度的惯性动捕系统&#xff0c;经常面临着超出…

筛斗数据全面解析数据提取与清洗的重要性

筛斗数据全面解析数据提取与清洗的重要性 在数字化时代&#xff0c;数据是企业决策的重要依据。然而&#xff0c;数据并非总是以我们期望的形式出现&#xff0c;它们可能分散、冗余、错误甚至不完整。因此&#xff0c;数据提取与清洗成为数据处理流程中不可或缺的两个环节。筛…

数学建模 —— 矩阵的运算(上)

目录 调用函数运算 sum : 求和函数 prod : 求乘积函数(product) cumsum : 计算累积和(cumulative sum) diff : 计算差分(difference) mean : 计算平均值 (average) median : 计算中位数 mode : 计算众数 var : 计算方差 (variance) std : 计 算 标 准 差 (standard d…

UE5(c++)开发日志(2):向日志和屏幕输出信息/将C++类变为蓝图类

通过UE_LOG()向日志输出&#xff0c;向其传入三个参数(①输出日志类型,一般LogTemp ②具体种类&#xff0c;普通/警告/错误&#xff0c;这里是Warning ③输出的文本内容&#xff0c;使用TEXT(" ")&#xff0c;这里是TEXT("Run AActor BeginPlay")) 代码写…

找茬找不同看图猜谜语小游戏

找茬找不同看图猜谜语小游戏&#xff1a;挑战你的观察力与智慧 &#x1f50d; 挑战你的观察力&#xff1a;找茬找不同 你是否曾对两张看似相同的图片感到好奇&#xff0c;想要找出它们之间的微妙差异&#xff1f;找茬找不同小游戏正是为了满足你的这种好奇心而设计的。在这些游…

前端基础:CSS(篇一)

目录 css概述 CSS与HTML的关系 基本语法 行内样式表 代码 运行 内嵌样式表 代码 运行 外部样式表 代码 运行 选择器 标签选择器 代码 运行 id选择器 代码 运行 类选择器 代码 运行 选择器优先问题 通配选择器 选中所有的标签 代码 运行 选择器组…

记一次 .NET某网络边缘计算系统 卡死分析

一&#xff1a;背景 1. 讲故事 早就听说过有什么 网络边缘计算&#xff0c;这次还真给遇到了&#xff0c;有点意思&#xff0c;问了下 chatgpt 这是干嘛的 ? 网络边缘计算是一种计算模型&#xff0c;它将计算能力和数据存储位置从传统的集中式数据中心向网络边缘的用户设备、…

spring boot(学习笔记第十一课)

spring boot(学习笔记第十一课) Session共享&#xff0c;JPA实现自动RESTful 学习内容&#xff1a; Session共享JPA实现自动RESTful 1. Session共享 Session共享面临问题 spring boot默认将session保存在web server的内存里面&#xff0c;会产生什么问题呢。 如上图所示&#…

BUU CODE REVIEW 11 代码审计之反序列化知识

打开靶场&#xff0c;得到的是一段代码。 通过分析上面代码可以构造下面代码&#xff0c;获取到序列化之后的obj。 <?php class BUU {public $correct "";public $input "";public function __destruct() {try {$this->correct base64_encode(u…

一文get懂kwai短视频助力巴西博弈slots游戏广告优势

一文get懂kwai短视频助力巴西博弈slots游戏广告优势 在数字化时代&#xff0c;短视频广告凭借其独特的魅力和高效的传播方式&#xff0c;成为了各大品牌进行营销推广的重要手段。特别是在巴西这个充满活力的国家&#xff0c;kwai短视频广告以其独特的方式&#xff0c;为博弈游…

电子技术基础(模电部分)笔记

终于整理出来了&#xff0c;可以安心复习大物线代了&#xff01;&#xff01; 数电部分预计7.10出

自动雪深传感器的类型

TH-XL2随着科技的飞速发展&#xff0c;气象监测技术也在不断进步。在降雪天气频发的冬季&#xff0c;雪深数据对于保障道路交通、农业生产和电力供应等具有至关重要的作用。自动雪深传感器作为气象监测的重要工具&#xff0c;其类型多样、功能各异&#xff0c;为气象数据的准确…

Android开发系列(十二)Jetpack Compose之BottomSheet

BottomSheet 是 Android 中一个常用的 UI 组件&#xff0c;它通常用于显示从屏幕底部弹出的用户界面。Jetpack Compose 是 Android 中的一个全新 UI 工具包&#xff0c;它提供了一种声明式的方式来构建用户界面。Jetpack Compose 中也有一个名为 BottomSheet 的组件&#xff0c…

基于STM32的智能门锁控制系统

目录 引言环境准备智能门锁控制系统基础代码实现&#xff1a;实现智能门锁控制系统 4.1 数据采集模块4.2 数据处理与分析4.3 控制系统实现4.4 用户界面与数据可视化应用场景&#xff1a;门锁管理与优化问题解决方案与优化收尾与总结 1. 引言 智能门锁控制系统通过使用STM32嵌…

NewspaceGPT带你玩系列之登录页

目录 注册一个账号&#xff0c;用qq邮箱&#xff0c;然后登录选一个可用的Plus&#xff0c;不要选3.5探索GPT今天的主角是HubSpot的登录页创建者问答继续问&#xff1a;答继续交流答看看结果&#xff0c;我有点崩溃重新简单来一次试试&#xff0c;下面开始一个新的登录页请求问…

昇思25天学习打卡营第5天|网络与模型相关要素探讨

目录 从 MindSpore 模块中导入nn和ops 定义模型类 模型层 nn.Flatten nn.Dense nn.ReLU nn.SequentialCell nn.Softmax 模型参数 从 MindSpore 模块中导入nn和ops 将 MindSpore 整个模块引入到当前的 Python 脚本里&#xff0c;方便后续运用 MindSpore 所提供的各类功能…

基于python的房价多元线性回归分析

1.导入必要的库 import pandas as pd import numpy as np import statsmodels.api as sm from sklearn.model_selection import train_test_split from sklearn.metrics import r2_score import matplotlib.pyplot as plt # 忽略Matplotlib的警告&#xff08;可选&…

GP37-S-N、GP37-S-E、GP37-S-R比例电磁铁驱动放大器

比例阀用电磁铁EP45-C、EP37-E、EP45-G、EP45-N、GP37-3-A、GP37-S-N、GP37-S-E、GP37-S-R在直流12V/24V的电液比例控制系统中与BEUEC比例控制放大器配套使用&#xff0c;共同作用于比例阀的控制。电磁铁输出力通过负载弹簧转换成位移&#xff0c;实现电流-力-位移线性转换&…