MYSQL实现原理 - 事务的隔离级别

news2025/2/14 3:38:22

版本

版本日期说明
v12025-02-10

准备

为后续故事的顺利展开,这里创建一个账户表

create database test;

CREATE TABLE `test`.`account` (
  `user_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '用户ID',
  `user_name` varchar(200) NOT NULL DEFAULT '' COMMENT '用户名称',
  `amount` int unsigned NOT NULL DEFAULT 0 COMMENT '金额',
  `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updated` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='账户表';

事务

MySQL事务是数据库管理系统执行过程中的一个逻辑单位,它由一组在数据库中执行的操作构成,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位

示例

账户1和账户2进行100元交易

-- 开启事务
begin;

-- 账户1向账户2转100元
update test.account set amount = amount- 100 where user_id = 1 and amount > 100;

update test.account set amount = amount + 100 where user_id = 2;

-- 提交事务
commit ;

ACID特性

  • 原子性(Atomicity):事务是一个原子操作单元,事务中的操作要么全部成功执行,要么全部失败回滚,以保证数据库状态的完整性。
  • 一致性(Consistency):事务的执行必须使数据库从一个一致状态转换到另一个一致状态,满足所有业务规则和约束条件。
  • 隔离性(Isolation):并发事务的执行不能相互干扰,每个事务在逻辑上都是独立的,以保证数据并发访问的正确性。
  • 持久性(Durability):一旦事务提交成功,其对数据库所做的更改就是永久性的,即使系统发生故障也能恢复。

理论上在某个事务对某个数据进行访问时,其他事务应该进行排队,当该事务提交之后,其他事务才可以继续访问这个数据。但是这样子的话对性能影响太大,我们肯定希望能最大可能的并行处理更多的事务,那么事务并发执行会引入哪些问题呐?

事务并发执行会遇到的问题

脏写(Dirty Write)

如果 一个事务修改了另一个未提交事务修改过的数据 ,那就意味着发生了 脏写 ,示意图如下:
脏写示意图
发生时间编号Session ASession B
1
begin;
2
begin;
3
update test.account set amount = 100 where user_id = 2;
4
update test.account set amount = 200 where user_id = 2;
5
commit ;
6
rollback ;
如上图, Session A Session B 各开启了一个事务, Session B 中的事务先将 user_id = 2 的记录中 amount 置为100   ,然后 Session B 中的事务将 user_id = 2 的记录中 amount 置为200   。如果之后 Session B 中的事务进行了回滚,那么 Session A 中的更新也将不复存在,这种现象就称
之为脏写,这时候Session A 就很懵逼,明明把amount 置为了200,也进行了提交,但是最后数据显示啥都没干  

脏读(Dirty Read)

如果 一个事务读到了另一个未提交事务修改过的数据 ,那就意味着发生了 脏读
脏读示意图
发生时间编号Session ASession B
1
begin;
2
begin;
3
update test.account set amount = 300 where user_id = 2;
4
select amount from test.account  where user_id = 2;

如果读到了amount = 300 则说明读到了脏读

5
commit ;
6
rollback ;
如上图, Session A Session B 各开启了一个事务, Session B 中的事务先将 user_id = 2 的记录中 amount 置为300  ,如果 Session B 中的事务读取 user_id = 2 的记录中 amount为300 。而 Session B 中的事务进行了回滚,那么 Session A 中相当于读到了一个不存在的数据,这种现象就称 之为脏读

不可重复度(Non-Repeatable Read

如果 一个事务只能读到另一个已经提交的事务修改过的数据,并且其他事务每对该数据进行一次修改并提交后,该事务都能查询得到最新值 ,那就意味着发生了 不可重复读
不可重复读示意图
发生时间编号Session ASession B
1
begin;
2
select amount from test.account  where user_id = 2;

此时读到的 amount 值为 100 

3
update test.account set amount = 300 where user_id = 2;
4
select amount from test.account  where user_id = 2;

如果此时读到的 amount 值为 300 ,说明发生了不可重复读

5
update test.account set amount = 400 where user_id = 2;
6
select amount from test.account  where user_id = 2;

如果此时读到的 amount 值为 400 ,说明发生了不可重复读

如上图,我们在 Session B 中提交了几个隐式事务(注意是隐式事务,意味着语句结束事务就提交了),这些事务都修改了 user_id = 2 的记录中 amount 的值 ,每次事务提交之后,如果 Session A 中的事务都可 以查看到最新的值,这种现象也被称之为 不可重复读

幻读(Phantom

如果 一个事务先根据某些条件查询出一些记录,之后另一个事务又向表中插入了符合这些条件的记录,原先的事务再次按照该条件查询时,能把另一个事务插入的记录也读出来 ,那就意味着发生了 幻读
幻读示意图
发生时间编号Session ASession B
1
begin;
2
select count(*) from test.account where user_id > 0;

假设此时读到的数量为100

3
insert into test.account (user_name, amount) values ("张三", 0);
4
select count(*) from test.account where user_id > 0;

如果此时读到的数量为101,说明发生了幻读

如上图,在Session  A中开启事务后,进行一次查询,会返回此时的数据数量,之后通过Session B插入数据,然后再 通过 Session  A 进行一次查询,如果次数读取到的数量包含了 Session B插入的数据,说明发生了幻读


有的同学就会疑惑了,在 Session  A 的事务中,Session  B新增了数据会导致幻读现象,那么如果删除了数据呐,是不是也算幻读呐?这里需要明确的是这种现象不叫幻读,幻读强调的是在一个事务中相同条件下多次读取,后者读到了前者没有读到的数据,针对的是新增记录情况。如果针对数据进行了删除操作,这是对每一条被删除的数据都发生了不可重复读现象


事务的隔离级别

以上几个事务并发问题按照严重程度来排序,是以下顺序:

脏写 > 脏读 > 不可重复读 > 幻读

 针对事务并发执行遇到的这些问题,结合使用场景,舍弃部分隔离性,在sql标准中添加了四种隔离级别:

  • READ UNCOMMITTED :未提交读。
  • READ COMMITTED :已提交读。
  • REPEATABLE READ :可重复读。
  • SERIALIZABLE :可串行化。

隔离级别与事务并发问题可发生情况,具体如下:

隔离级别脏写脏读不可重复读幻读
READ UNCOMMITTEDFTTT
READ COMMITTEDFFTT
REPEATABLE READFFFT
SERIALIZABLEFFFF
也就是说:
  • READ UNCOMMITTED 隔离级别下,可能发生 脏读 不可重复读 幻读 问题。
  • READ COMMITTED 隔离级别下,可能发生 不可重复读 幻读 问题。
  • REPEATABLE READ 隔离级别下,可能发生 幻读 问题。
  • SERIALIZABLE 隔离级别下,各种问题都不可以发生。
MySQL 的默认隔离级别为 REPEATABLE READ

如何设置事务的隔离级别

通过SQL语句设置

我们可以通过下边的语句修改事务的隔离级别:
SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL level;

其中的 level 可选值有4个:

level: {
 REPEATABLE READ
 | READ COMMITTED
 | READ UNCOMMITTED
 | SERIALIZABLE
}
GLOBAL、SESSION 

这两个关键字可以对不同范围的事务产生不同的影响,也可以为空

GLOBAL

全局范围影响

用法:

SET GLOBAL TRANSACTION ISOLATION LEVEL level;

影响范围:

  • 只对执行完该语句之后产生的会话起作用
  • 当前已经存在的会话无效
 SESSION

会话范围影响

用法:

SET SESSION TRANSACTION ISOLATION LEVEL level;

影响范围:

  • 对当前会话的所有后续的事务有效
  • 该语句可以在已经开启的事务中间执行,但不会影响当前正在执行的事务
  • 如果在事务之间执行,则对后续的事务有效
无关键词

只对执行语句的下一个事务产生影响

用法:

SET TRANSACTION ISOLATION LEVEL level;

影响范围:

  • 对当前会话的下一个将开启的事务有效
  • 下一个事务执行完,后续的事务将恢复默认隔离级别
  • 该语句不能在已开启的事务中执行,会报错
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
ERROR 1568 (25001): Transaction characteristics can't be changed while a transaction is in progress
mysql> 

服务器启动时设置

可以通过修改启动参数 transaction-isolation 的值 来修改mysql服务器的默认隔离级别。

比如启动时添加如下参数设置,那么MYSQL的默认隔离级别隔离级别就会从原来的

REPEATABLE READ 变成了 SERIALIZABLE
--transaction-isolation=SERIALIZABLE

通过设置系统变量修改

可以通过修改系统变量transaction_isolation的方式来设置事务的隔离级别,详情见:

https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_transaction_isolation

查看当前会话的默认隔离级别

mysql> SHOW VARIABLES LIKE 'transaction_isolation';
+-----------------------+-----------------+
| Variable_name         | Value           |
+-----------------------+-----------------+
| transaction_isolation | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set (0.02 sec)

mysql> 

或者

mysql> SELECT @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| REPEATABLE-READ         |
+-------------------------+
1 row in set (0.00 sec)

mysql> 

Innodb 不同事务隔离级别的实现方式

读未提交(Read Uncommitted)

  • 在这种隔离级别下,SELECT语句不会加锁,因此可能会读取到未提交事务修改的数据,即“读脏”。
  • 这是并发性最高但一致性最差的隔离级别,因为它允许读取到其他事务尚未提交的数据

串行化(Serializable)

  • 在这种隔离级别下,所有的SELECT语句都会被隐式地转化为SELECT ... IN SHARE MODE,这意味着如果有未提交的事务正在修改某些行,那么所有读取这些行的SELECT语句都会被阻塞。
  • 这是一致性最好的隔离级别,但并发性最差,因为它将事务完全串行化,从而避免了所有并发问题。
  • 在这种隔离级别下,UPDATEDELETE操作也会与其他事务互斥,即同一时间只能有一个事务对这些数据进行修改。

可重复读(Repeated Read, RR)

  • 这是InnoDB的默认隔离级别。
  • 在这种隔离级别下,普通的SELECT语句使用快照读(snapshot read),这是一种不加锁的一致性读,底层使用MVCC来实现。
  • 加锁的SELECT(如SELECT ... IN SHARE MODESELECT ... FOR UPDATE)、UPDATEDELETE等语句的锁策略取决于查询条件:
    • 如果在唯一索引上使用唯一的查询条件,那么会使用记录锁(record lock),而不会封锁记录之间的间隔。
    • 如果使用范围查询条件,那么会使用间隙锁(gap lock)和临键锁(next-key lock),以锁住索引记录之间的范围,避免范围间插入记录,从而避免产生幻影行记录和不可重复的读。

读提交(Read Committed, RC)

  • 这是互联网最常用的隔离级别。
  • 在这种隔离级别下,普通的SELECT语句也是快照读,但加锁的SELECTUPDATEDELETE等语句的锁策略与可重复读有所不同:
    • 除了在外键约束检查和重复键检查时会封锁区间外,其他时刻都只使用记录锁。
    • 这意味着其他事务的插入操作仍然可以执行,因此可能会导致读取到幻影记录。

总结

本篇讲述了引入事务隔离级别的原因,及事务隔离级别的查看和设置方式,事务隔离级别的底层实现逻辑描述需要更长篇幅,这里先做简述,后续会逐块展开讲解

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

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

相关文章

MindStudio制作MindSpore TBE算子(四)算子测试(ST测试-Ascend910B/ModelArts)--失败尝试

上一节,MindStudio制作MindSpore TBE算子(三)算子测试(ST测试),因此缺乏对应的硬件环境导致无法进行ST测试,导致难以自安,今天搞来Ascend910B服务器来填坑,看看是否是硬件…

transformer 基础知识

概要:简要记录 Encoder-Decoder 架构、seq2seq 模型、Attention 机制 Encoder & Decoder encoder 接收输入,生成一个固定长度的上下文向量(编码器生成的最终隐藏状态);decoder 接收上下文向量(或状态…

机器学习(李宏毅)——self-Attention

一、前言 本文章作为学习2023年《李宏毅机器学习课程》的笔记,感谢台湾大学李宏毅教授的课程,respect!!! 二、大纲 何为self-Attention?原理剖析self-Attention VS CNN、RNN、GNN 三、何为self-Attenti…

实现限制同一个账号最多只能在3个客户端(有电脑、手机等)登录(附关键源码)

如上图,我的百度网盘已登录设备列表,有一个手机,2个windows客户端。手机设备有型号、最后登录时间、IP等。windows客户端信息有最后登录时间、操作系统类型、IP地址等。这些具体是如何实现的?下面分别给出android APP中采集手机信…

伺服报警的含义

前言: 大家好,我是上位机马工,硕士毕业4年年入40万,目前在一家自动化公司担任软件经理,从事C#上位机软件开发8年以上!我们在开发C#的运动控制程序的时候,一个必要的步骤就是设置伺服报警信号的…

蓝桥杯-洛谷刷题-day5(C++)(为未完成)

1.P1328 [NOIP2014 提高组] 生活大爆炸版石头剪刀布 i.题目 ii.代码 #include <iostream> #include <string> using namespace std;int N, Na, Nb; //0-"剪刀", 1-"石头", 2-"布", 3-"蜥", 4-"斯"&#xff1…

LVS 负载均衡集群(NAT模式)

一、环境准备 四台主机&#xff08;一台 LVS、两台 RS、一台客户端&#xff09; 1.1.LVS 主机 LVS 主机&#xff08;两块网卡&#xff09; 第一块&#xff1a;NAT模式&#xff08;内网&#xff09; 第二块&#xff1a;添加网卡&#xff08;仅主机模式&#xff09;&#xff0…

解决 DeepSeek 官网服务器繁忙的实用方案

解决 DeepSeek 官网服务器繁忙的实用方案 大家在使用 DeepSeek 时&#xff0c;是不是经常遇到官网服务器繁忙&#xff0c;等半天都加载不出来的情况&#xff1f;别担心&#xff0c;今天就给大家分享一个用 DeepSeek 硅基流动 Cherry Studio 解决这个问题的实用方案&#xff…

嵌入式八股文面试题(二)C语言算法

相关概念请查看文章&#xff1a;C语言概念。 1. 如何实现一个简单的内存池&#xff1f; 简单实现&#xff1a; #include <stdio.h> #include <stdlib.h>//内存块 typedef struct MemoryBlock {void *data; // 内存块起始地址struct MemoryBlock *next; // 下一个内…

#渗透测试#批量漏洞挖掘#LiveBos UploadFile 任意文件上传漏洞

免责声明 本教程仅为合法的教学目的而准备,严禁用于任何形式的违法犯罪活动及其他商业行为,在使用本教程前,您应确保该行为符合当地的法律法规,继续阅读即表示您需自行承担所有操作的后果,如有异议,请立即停止本文章读。 目录 漏洞背景 漏洞成因 影响评估 检测方案 …

ds-download-link 插件:以独特图标选择,打造文章下载链接

源码介绍 “ds-download-link”插件为 WordPress 网站提供了在文章编辑器中添加下载链接的功能&#xff0c;每个下载链接都支持图标选择&#xff0c;并能将这些链接以美观的样式展示在文章前端页面。以下是该插件的主要特性和功能&#xff1a; 后台功能 在文章编辑器下方添加…

判断函数是否为react组件或lazy包裹的组件

function Modal(){return <p>123</p> } 实参里填入函数名,是false 实参里填入标签形式的函数,是true isValidElement(Modal)//false isValidElement(<Modal></Modal>)//true 官方说明 isValidElement – React 中文文档 但是官方并不建议用isValidE…

PHP 中的除以零错误

除以零错误&#xff08;Division by zero&#xff09;是指数字除以零的情况&#xff0c; 这在数学上是未定义的。在 PHP 中&#xff0c;处理这种错误的方式取决于 PHP 版本&#xff1a; PHP 7&#xff1a; 使用 / 运算符会产生一个警告 (E_WARNING) 并返回 false。 使用 intd…

【QT】控件 -- 多元素类 | 容器类 | 布局类

&#x1f525; 目录 一、多元素类1. List Widget -- 列表2. Table Widget -- 表格3. Tree Widget -- 树形 二、容器类1. Group Box -- 分组框2. Tab Widget -- 标签页 三、布局类1. 垂直布局【使用 QVBoxLayout 管理多个控件】【创建两个 QVBoxLayout】 2. 水平布局【使用 QHBo…

NO.15十六届蓝桥杯备战|while循环|六道练习(C++)

while循环 while语法形式 while 语句的语法结构和 if 语句⾮常相似&#xff0c;但不同的是 while 是⽤来实现循环的&#xff0c; if 是⽆法实现循环的。 下⾯是 while 循环的语法形式&#xff1a; //形式1 while ( 表达式 )语句; //形式2 //如果循环体想包含更多的语句&a…

kotlin标准库里面也有很多java类

Kotlin 标准库中确实存在许多与 Java 类直接关联或基于 Java 类封装的结构&#xff0c;但这并不是“问题”&#xff0c;而是 Kotlin 与 JVM 生态深度兼容和互操作性的体现。以下从技术原理和设计哲学的角度详细解释&#xff1a; 一、Kotlin 与 JVM 的底层关系 Kotlin 代码最终…

Flutter 双屏双引擎通信插件加入 GitCode:解锁双屏开发新潜能

在双屏设备应用场景日益丰富的当下&#xff0c;移动应用开发领域迎来了新的机遇与挑战。如何高效利用双屏设备优势&#xff0c;为用户打造更优质的交互体验&#xff0c;成为开发者们关注的焦点。近日&#xff0c;一款名为 Flutter 双屏双引擎通信插件的创新项目正式入驻 GitCod…

01、单片机上电后没有正常运行怎么办

单片机上电后没有运转, 首先要检查什么? 1、单片机供电是否正常? &电路焊接检查 如果连最基本的供电都没有,其它都是空谈啊!检查电路断路了没有?短路了没有?电源合适吗?有没有虚焊? 拿起万用表之前,预想一下测量哪里?供电电压应该是多少?对PCB上电压测量点要…

使用 EMQX 接入 LwM2M 协议设备

LwM2M 协议介绍 LwM2M 是一种轻量级的物联网设备管理协议&#xff0c;由 OMA&#xff08;Open Mobile Alliance&#xff09;组织制定。它基于 CoAP &#xff08;Constrained Application Protocol&#xff09;协议&#xff0c;专门针对资源受限的物联网设备设计&#xff0c;例…

使用 mkcert 本地部署启动了 TLS/SSL 加密通讯的 MongoDB 副本集和分片集群

MongoDB 是支持客户端与 MongoDB 服务器之间启用 TLS/SSL 进行加密通讯的, 对于 MongoDB 副本集和分片集群内部的通讯, 也可以开启 TLS/SSL 认证. 本文会使用 mkcert 创建 TLS/SSL 证书, 基于创建的证书, 介绍 MongoDB 副本集、分片集群中启动 TLS/SSL 通讯的方法. 我们将会在…