【MySQL】事务实现原理
目录
事务
如何使用
ACID
原子性(Atomicity)
原子性实现原理
持久性(Durability)
持久性实现原理
隔离性
隔离级别
读未提交
读已提交
可重复读
串行化
隔离级别原理
锁
共享锁&独占锁
意向锁
索引记录锁
间隙锁
临键锁
插入意向锁
自增锁
MVCC
实现原理
一致性(Consistency)
事务
事务是将一组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 时该版本已经提交,可以直接访问(比如事务id=150);如果不是,则继续遍历并判断下一个版本。
这样查询,可以解决脏读的问题。
首先,幻读无法单独通过MVCC解决。
对于不可重复读问题,在事务中的第一个select时创建一个ReadView。后续的查询都使用这个ReadView来判断,因此每次查询的结果都是相同的,从而解决了不可重复读问题。也就是,在REPEATABLE READ(可重复读)隔离级别下,整个事务周期只使用第一个查询锁创建的ReadView。
如果事务每次查询都创建一个新的ReadView,那么就会出现不可重复读问题。在READ COMMITTED(读已提交)隔离级别下,就是采用这种实现方式。
一致性(Consistency)
保证了原子性、持久性和隔离性后,一致性也就自然而然的保证了。
相关文章:

【MySQL】事务实现原理
目录 事务 如何使用 ACID 原子性(Atomicity) 原子性实现原理 持久性(Durability) 持久性实现原理 隔离性 隔离级别 读未提交 读已提交 可重复读 串行化 隔离级别原理 锁 共享锁&独占锁 意向锁 索引记录锁 间隙锁 临键锁 插入意向锁 自增锁 MVCC 实现…...

面向物联网行业的异常监控追踪技术解决方案:技术革新与运维保障
在现代高度数字化和互联的环境中,物联网技术已经深入到我们生活的方方面面。特别是在家庭和工业环境中,物联网系列通讯作为连接各类设备的关键枢纽,其稳定性和可靠性显得尤为重要。本文将介绍一种创新的监控系统,旨在实时跟踪和分…...
守护厨房空气:全面排查与修复油烟净化器跳闸问题
我最近分析了餐饮市场的油烟净化器等产品报告,解决了餐饮业厨房油腻的难题,更加方便了在餐饮业和商业场所有需求的小伙伴们。 在繁忙的餐饮业厨房中,油烟净化器是确保空气清新和环境卫生的关键设备。然而,油烟净化器在长时间高强…...

【微服务网关——https与http2代理实现】
1.https与http2代理 1.1 重新认识https与http2 https是http安全版本http2是一种传输协议两者并没有本质联系 1.1.1 https与http的区别 HTTP(超文本传输协议)和 HTTPS(安全超文本传输协议)是用于在网络上交换数据的两种协议。H…...

mssql查询历史执行过的语句日志
SELECT deqs.creation_time,dest.text AS [SQL Text],deqs.execution_count,deqs.total_elapsed_time,deqs.total_worker_time FROM sys.dm_exec_query_stats AS deqs CROSS APPLY sys.dm_exec_sql_text(deqs.sql_handle) AS dest--where dest.text like %这个是我的条件&#…...
【LeetCode】每日一题:买卖股票的最佳时机 II
给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。 在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。 返回 你能获得的 最大 利润 。 AC代码 水…...

【TS】TypeScript 联合类型详解:解锁更灵活的类型系统
🌈个人主页: 鑫宝Code 🔥热门专栏: 闲话杂谈| 炫酷HTML | JavaScript基础 💫个人格言: "如无必要,勿增实体" 文章目录 TypeScript 联合类型详解:解锁更灵活的类型系统一、联合类型的定义二…...

kali改回官方源后更新失败
官方源: deb http://http.kali.org/kali kali-rolling main non-free contrib deb-src http://http.kali.org/kali kali-rolling main non-free contrib在文件 /etc/cat/sources.list中将官方源修改为: deb http://http.kali.org/kali kali-rolling ma…...
Mysql 左关联(LEFT JOIN)
在左关联(LEFT JOIN)操作中,关于大表和小表的连接顺序,通常建议将小表放在前面,大表放在后面。这种安排方式有助于提高查询效率,原因如下: 扫描效率:在SQL查询中,尤其是…...
[笔记]小米CyberDog机器狗仿真调试记录
从官方github的所有源码库来看,所有的source命令只有两条,执行它以配置环境变量: source /opt/ros/galactic/setup.bash source /home/cyberdog_ws/install/setup.bash 如果运行脚本之后gazebo正常启动及机器狗模型在悬空状态,问…...

第十四届蓝桥杯省赛C++B组G题【子串简写】题解(AC)
题目大意 给定字符串 s s s,字符 a , b a, b a,b,问字符串 s s s 中有多少个 a a a 开头 b b b 结尾的子串。 解题思路 20pts 使用二重循环枚举左端点和右端点,判断是否为 a a a 开头 b b b 结尾的字符串,是则答案加一…...
实现Java Web应用的高性能负载均衡方案
实现Java Web应用的高性能负载均衡方案 大家好,我是微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿! 在高并发的网络环境中,负载均衡是确保Web应用程序高性能和可靠性的关键策略之一。本文将探讨如何…...
医学预测模型web APP的制作建议
医学预测模型web APP的制作建议 医学预测模型类web APP定义为承载预测模型而便利预测模型临床应用的可视化客户端。 医学预测模型类web APP的功能是衔接预测模型和临床实践,让用户正确地,方便地使用预测模型并恰当地理解预测模型的结果,在此…...
gitlab每日备份以及restore
gitlab服务有非常简洁的每日备份命令, 从production的gitlab的每日备份中restore到backup环境也非常方便。 一、Production gitlab每日备份 1. Production gitlab环境上编写脚本 cat /root/gitlab_bak.shgitlab-rake gitlab:backup:create > /var/opt/gitl…...

2024-07-05 base SAS programming学习笔记9(variables)
1.在数据集增加累加变量值(SUM) 求和语句(SUM STATEMENT):variableexpression variable是累积求和的变量名,为数值型,默认初始值为0;该variable值则会保留到一个观测 当expression有缺失值,在求…...

kafka--发布-订阅消息系统
1. Kafka概述 1. kafka是什么 kafka是分布式的、高并发的、基于发布/订阅模式的消息队列软件系统。 kafka中的重要组件 Producer:消息生产者,发布消息到Kafka集群的终端或服务Consume:消费者,从Kafka集群中消费消息的终端或服…...
2024最新软件测试面试题。内附答案+文档
🍅 视频学习:文末有免费的配套视频可观看 🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 1、你以前工作时的测试流程是什么? 参考答案:(灵活回答&…...

新加坡很火的slots游戏代投Facebook广告新流量趋势
新加坡很火的slots游戏代投Facebook广告新流量趋势 在新加坡这片充满活力的土地上,Slots游戏以其独特的魅力和吸引力,迅速成为了许多玩家的心头好。而Facebook,作为全球最大的社交媒体平台之一,为Slots游戏的推广提供了得天独厚的…...
C++ 实现字符串逆序
C 实现字符串逆序 思路: 输入一个字符串。使用双指针法,交换字符串的首尾字符,逐步向中间移动。输出逆序后的字符串。 #include <iostream> #include <string>using namespace std;void reverseString(string &str) {int …...

【项目实践】贪吃蛇
一、游戏效果展示二、博客目标三、使用到的知识四、Win32 API 介绍 4.1 WIn32 API4.2 控制台程序4.3 控制屏幕上的坐标COORD4.4 GetStdHandle4.5 GetConsoleCursorInfo 4.5.1 CONSOLE_CURSOR_INFO 4.6 SetConsoleCursorInfo4.7 SetConsoleCursorPosition4.8 GetAsyncKeyState 五…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...

深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...

ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA
浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求,本次涉及的主要是收费汇聚交换机的配置,浪潮网络设备在高速项目很少,通…...
Go 并发编程基础:通道(Channel)的使用
在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...
OD 算法题 B卷【正整数到Excel编号之间的转换】
文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的:a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...

Tauri2学习笔记
教程地址:https://www.bilibili.com/video/BV1Ca411N7mF?spm_id_from333.788.player.switch&vd_source707ec8983cc32e6e065d5496a7f79ee6 官方指引:https://tauri.app/zh-cn/start/ 目前Tauri2的教程视频不多,我按照Tauri1的教程来学习&…...
精益数据分析(98/126):电商转化率优化与网站性能的底层逻辑
精益数据分析(98/126):电商转化率优化与网站性能的底层逻辑 在电子商务领域,转化率与网站性能是决定商业成败的核心指标。今天,我们将深入解析不同类型电商平台的转化率基准,探讨页面加载速度对用户行为的…...

Java设计模式:责任链模式
一、什么是责任链模式? 责任链模式(Chain of Responsibility Pattern) 是一种 行为型设计模式,它通过将请求沿着一条处理链传递,直到某个对象处理它为止。这种模式的核心思想是 解耦请求的发送者和接收者,…...
codeforces C. Cool Partition
目录 题目简述: 思路: 总代码: https://codeforces.com/contest/2117/problem/C 题目简述: 给定一个整数数组,现要求你对数组进行分割,但需满足条件:前一个子数组中的值必须在后一个子数组中…...