当前位置: 首页 > news >正文

MySQL学习笔记(一)数据库事务隔离级别与多版本并发控制(MVCC)

一、数据库事务隔离级别

数据库事务的隔离级别有4种,由低到高分别为Read uncommitted (读未提交)、Read committed(读提交) 、Repeatable read(可重复读) 、Serializable (串行化)。

1、脏读、不可重复读、幻读

在数据库多个事务并发执行的情况下,不同的隔离级别可能会出现脏读、不可重复读、幻读这几种问题。

脏读:读取到未提交的数据。事务A对某条数据进行了修改,但还未提交,这时事务B读取到了这条未提交的数据。如果事务A出错回滚,事务B读取到的数据就是一条脏数据。
不可重复读: 同一事务内多次查询某条数据的结果不一致。事务A对某条数据执行了两次查询,在这两次查询的时间间隔内,事务B对该条数据执行了修改(update),导致事务A的两次查询结果不一致。
幻读: 同一事务内多次查询的结果集不一致(多或少了几条数据)。事务A以相同的查询条件执行了两次查询,在这两次查询的时间间隔内,事务B新增或删除(insert|delete)了几条数据,导致事务A的两次查询结果集不一致。

2、四种隔离级别

读未提交 (Read uncommitted)
最低隔离级别,允许事务读取其他事务未提交的更改。可能导致脏读、不可重复读、幻读问题。

读提交(Read Committed)
事务只能读取到已提交的数据。可以解决脏读问题,但是还会有可能不可重复读、幻读问题。

可重复读(Repeated Read)
专门针对“不可重复读”这种情况而制定的隔离级别。可以解决脏读和不可重复读问题,但还是有可能出现幻读问题。MYSQL默认的隔离级别

串行化(Serializable)
最高隔离级别,所有事务都串行化顺序执行,不存在并发,从而完全防止脏读、不可重复读和幻读问题。但是这种隔离级别性能较低,基本不会使用。

二、多版本并发控制(MVCC)

1、什么是多版本并发控制(MVCC)

多版本并发控制英文全称是 Multiversion Concurrency Control,简称 MVCC。

MVCC是通过保存数据行的历史版本,组成版本链,对比事务ID与版本号来确定当前事务应该使用哪个历史版本数据,而无需加锁就可以保证事务的隔离效果,可以认为是一种不加锁的行锁,可以提高数据库的性能。

MySQL的大多数事务型存储引擎实现的都不是简单的行级锁。基于提升并发性能的考虑,它们一般都同时实现了多版本并发控制(MVCC)。不仅是MySQL,包括Oracle、PostgreSQL等其他数据库系统也都实现了MVCC,但各自的实现机制不尽相同,因为MVCC没有一个统一的实现标准,典型的有乐观(optimistic)并发控制和悲观(pessimistic)并发控制。

2、MVCC可以解决哪些问题

(1)读写之间阻塞的问题
MVCC可以让读写操作互不阻塞,即读不阻塞写,写不阻塞读。相较于普通锁(串行运行)、读写锁(可以实现读读并发),MVCC提升数据库事务并发处理能力。
(2)降低了死锁的概率
MVCC大多数情况不用加锁,写数据时也只是加行锁,降低了死锁的概率
(3) 解决一致性读的问题
一致性读也叫快照读,当查询数据库在某个时间点的快照时,只能看到这个时间点之前事务提交更新的结果,或者本事务提交的结果,而不能看到这个时间点之后事务提交的更新的结果。

3、快照读与当前读

在MySQL InnoDB中,MVCC主要是为了提高数据库并发性能,以更好的方式处理读-写冲突,不用加锁实现并发读-写。这个读就是快照读,而非当前读,当前读实际上是一种加锁的操作,是悲观锁的实现。而MVCC本质是采用乐观锁思想的一种方式。
(1)快照读
快照读又叫一致性读,读取的是快照数据。是一种一致性不加锁的读。不加锁的简单的 SELECT 都属于快照读。例如:

SELECT * FROM t WHERE id=1

快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本。快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读。
(2)当前读
当前读读取的是记录的最新版本(最新数据,而不是历史版本的数据),读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。加锁的 SELECT,或者对数据进行增删改都会进行当前读。

SELECT * FROM t LOCK IN SHARE MODE; # 共享锁SELECT * FROM t FOR UPDATE; # 排他锁INSERT INTO t values ... # 排他锁DELETE FROM t WHERE ... # 排他锁UPDATE t SET ... # 排他锁

4、InnoDB的MVCC原理

(1)隐藏字段和Undo Log版本链

隐藏字段:
对于使用 InnoDB 存储引擎的表来说,它的聚簇索引的叶子节点为数据页,存放的就是整张表的数据行,而每个数据行中有一些重要的隐藏字段。

  • DB_ROW_ID:大小为6字节,默认主键列,如果表没有设置主键,InnoDB会自动生成该隐藏主键列。
  • DB_TRX_ID:大小为6字节,生成该行数据历史版本的事务ID。
  • DB_ROLL_PTR:大小为7字节,回滚指针,指向上一个版本的Undo log,InnoDB 便是通过这个指针找到之前版本的数据,该行数据的所有版本记录都在undo中通过链表形式组织在一起。

另外每条记录的头信息(record header)里都有一个专门的 bit(deleted_flag)来表示当前记录是否已经被删除
Undo log版本链:
每次对数据行进行改动,都会生成一条undo日志,每条undo日志也都有一个 DB_ROLL_PTR属性,INSERT 操作对应的undo日志没有该属性,因为该记录并没有更早的版本。版本链示例如下:
在这里插入图片描述

(2)ReadView

在多事务并发修改同一行数据的情况下,MVCC通过隐藏字段和Undo Log生成了该行记录的历史版本快照,并组成了版本链。但是事务读取数据时该读取哪个版本的数据,需要一个读取机制来确定。这时就用到了ReadView(可读视图)
设计思路:
(1)使用 READ UNCOMMITTED 隔离级别的事务,由于可以读到未提交事务修改过的记录,所以直接读取记录的最新版本就好
(2)使用 SERIALIZABLE 隔离级别的事务,InnoDB规定使用加锁的方式来访问记录
(3)使用 READ COMMITTED 和 REPEATABLE READ 隔离级别的事务,都必须保证读到已经提交了的 事务修改过的记录。假如另一个事务已经修改了记录但是尚未提交,是不能直接读取最新版本的记录的,核心问题就是需要判断一下版本链中的哪个版本是当前事务可见的,这是ReadView要解决的主要问题

ReadView内容:
ReadView主要包含以下4个内容:
(1)creator_trx_id
创建当前ReadView的事务ID。注意只有在对表中的记录做改动时(执行INSERT、DELETE、UPDATE这些语句时)才会为事务分配事务ID,否则在一个只读事务中的事务ID值都默认为0。
(2)trx_ids
生成ReadView时,当前系统活跃的事务ID集合,活跃的事务表示还未提交的事务。
(3)up_limit_id
活跃的事务中,最小的事务ID
(4)low_limit_id
表示生成ReadView时系统中应该分配给下一个事务的 id 值,即最大事务ID+1。

ReadView的规则:
(1)如果某行数据被访问版本的trx_id属性值与ReadView中的 creator_trx_id 值相同,意味着当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问。

(2)如果某行数据被访问版本的trx_id属性值小于ReadView中的 up_limit_id值,表明生成该版本的事务在当前事务生成ReadView前已经提交,所以该版本可以被当前事务访问。

(3)如果某行数据被访问版本的trx_id属性值大于或等于ReadView中的 low_limit_id 值,表明生成该版本的事务在当前事务生成ReadView后才开启,所以该版本不可以被当前事务访问。

(4)如果某行数据被访问版本的trx_id属性值在ReadView的 up_limit_id 和 low_limit_id 之间,那就需要判断一下trx_id属性值是不是在 trx_ids 列表中。
a. 如果在,说明创建ReadView时生成该版本的事务还是活跃的,该版本不可以被访问
b. 如果不在,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被访问

(3)MVCC快照读的整体流程

当查询一条记录的时候,系统通过如下流程MVCC找到合适的记录:

(1)首先获取事务自己的版本号,也就是事务 ID

(2)获取 ReadView

(3)查询得到的数据,然后与 ReadView 中的事务版本号进行比较

(4)如果不符合 ReadView 规则,就需要从 Undo Log 中获取历史快照

(5)最后返回符合规则的数据

如果某个版本的数据对当前事务不可见的话,那就顺着版本链找到下一个版本的数据,继续按照上边的步骤判断可见性。以此类推,直到版本链中的最后一个版本。如果最后一个版本也不可见的话,那么就意味着该条记录对该事务完全不可见,查询结果就不包含该记录。

(4)MVCC解决不可重复读和幻读原理

当隔离级别为读提交(Read Committed)时,一个事务中的每一次 SELECT 查询都会重新获取一次ReadView。当隔离级别为可重复读(Repeated Read)时,一个事务只在第一次 SELECT 的时候会获取一次ReadView,而后面所有的SELECT都会复用这个ReadView。

1)READ COMMITTED隔离级别

假设事务A、B的事务ID分别为10、20,并且事务正在执行,还未提交

# Transaction A ID=10
BEGIN;
UPDATE t SET name="李四" WHERE id=1;
UPDATE t SET name="王五" WHERE id=1;
# Transaction B iD=20
BEGIN;
UPDATE t SET name="赵六" WHERE id=1;

此时id为1的数据的版本链如下:
在这里插入图片描述
此时,另一个事务C开始执行

# 使用READ COMMITTED隔离级别的事务C ID=21
BEGIN;
# 事务A和B都未提交
# SELECT1
SELECT * FROM t WHERE id = 1; # 得到的列name的值为'张三'

SELECT1生成的ReadView的trx_ids=[10,20,21],up_limit_id=10,low_limit_id=22,creator_trx_id为0。从“赵六”那个版本开始查找,因为10、20都在trx_ids里,所以符合的版本只有trx_id=5的版本数据。

此时,把事务A提交

# Transaction A ID=10
BEGIN;
UPDATE student SET name="李四" WHERE id=1;
UPDATE student SET name="王五" WHERE id=1;
COMMIT;

然后在事务C中继续查询

# 使用READ COMMITTED隔离级别的事务C ID=21
BEGIN;
# 事务A和B都未提交
# SELECT1
SELECT * FROM t WHERE id = 1; # 得到的列name的值为'张三'
#SELECT2
SELECT * FROM t WHERE id = 1; # 得到的列name的值为'王五'

SELECT2会生成新的ReadView,新ReadView的trx_ids=[20,21],up_limit_id=20,low_limit_id=22,creator_trx_id为0。还是从“赵六”那个版本开始查找,此时只有20在trx_ids里。所以trx_id=10的版本对SELECT2是可见的,SELECT2查找到的结果就是“王五”那个版本数据。

2)REPEATABLE READ隔离级别

操作同上。但是在SELECT2时,REPEATABLE READ隔离级别是不会重新生成ReadView的,所以SELECT2使用的还是SELECT1的ReadView。所以查出来的结果和SELECT1是一致的,都是trx_id=5的版本数据。这样就解决了不可重复读的问题。

(5)MVCC解决幻读原理

假设表t中有一条id为1的数据,trx_id=10
在这里插入图片描述

现在有事务A(ID=20)、事务B(ID=30)并行执行

# Transaction A ID=20
BEGIN;
#SELECT1
SELECT * FROM t WHERE id >= 1; 

SELECT创建的ReadView的内容为trx_ids=[20,30] , up_limit_id=20 , low_limit_id=31 , creator_trx_id=0。根据ReadView的规则,id=1的数据的trx_id=10<up_limit_id,所以事务A能查出来。

此时,事务B插入两条数据,并提交

# Transaction B ID=30
BEGIN;
insert into t(id,name) values(2,'李四');
insert into t(id,name) values(3,'王五');
COMMIT;

然后,事务A再次查询

# Transaction A ID=20
BEGIN;
#SELECT1
SELECT * FROM t WHERE id >= 1; 
#SELECT2
SELECT * FROM t WHERE id >= 1; 

因为是REPEATABLE READ隔离级别,SELECT2不创建新的ReadView,使用SELECT1的ReadView。ReadView的内容为trx_ids=[20,30] , up_limit_id=20 , low_limit_id=31 , creator_trx_id=0。因为id为2,3的两条数据的trx_id=30,在trx_ids里,所以事务A不能查出来。
在这里插入图片描述
所以MYSQL的InnoDB在REPEATABLE READ隔离级别下,不会出现幻读的情况。

相关文章:

MySQL学习笔记(一)数据库事务隔离级别与多版本并发控制(MVCC)

一、数据库事务隔离级别 数据库事务的隔离级别有4种&#xff0c;由低到高分别为Read uncommitted &#xff08;读未提交&#xff09;、Read committed&#xff08;读提交&#xff09; 、Repeatable read&#xff08;可重复读&#xff09; 、Serializable &#xff08;串行化&a…...

如何在Linux上为PyCharm创建和配置Desktop Entry

在Linux操作系统中&#xff0c;.desktop 文件是一种桌面条目文件&#xff0c;用于在图形用户界面中添加程序快捷方式。本文将指导您如何为PyCharm IDE创建和配置一个 .desktop 文件&#xff0c;从而能够通过应用程序菜单或桌面图标快速启动PyCharm。 步骤 1: 确定PyCharm安装路…...

Igraph入门指南 4

二、图的创建 图分有向图和无向图&#xff0c;所以图的创建有各自的实现方式。 1、手工创建图&#xff1a; 1-1 通过文本创建&#xff1a;graph_from_literal 通过每项提供两个顶点名&#xff08;或ID号&#xff09;作为一条边的格式&#xff0c;手动创建图&#xff0c;顶点…...

外包干了30天,技术明显退步。。

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 这次来聊一个大家可能也比较关心的问题&#xff0c;那就是就业城市选择的问题。而谈到这个问题&a…...

数据库 — 增删查改

一、操作数据库、表 显示 show databases;创建 create database xxx;使用 use xxx; 删除 drop database xxx;查看表&#xff1b; show tables; 查看表结构 desc 表名; 创建 create table 表名(字段1 类型1&#xff0c;字段2 类型2&#xff0c;.... ); 删除 drop table 表名; 二…...

eclipse搭建java web项目

准备条件 eclipsejdk1.8 &#xff08;配置jdk环境&#xff09;apache-tomcat-8.5.97&#xff08;记住安装位置&#xff09; 一 点击完成 开始创建javaweb项目 import java.io.IOException; import java.io.PrintWriter;import javax.servlet.ServletException; import javax.s…...

gitlab-ci_cd语法CICD

工作原理 1、将代码托管在git 2、在项目根目录创建ci文件.gitlan-ci.yml 在文件中指定构建&#xff0c;测试和部署脚本 3、gitlab将检测到他并使用名为git Runner的工具运行脚本 4、脚本被分组为作业&#xff0c;他们共同组成了一个管道gitlab-ci的脚本执行&#xff0c;需要自…...

python 蓝桥杯之动态规划入门

文章目录 DFS滑行&#xff08;DFS 记忆搜索&#xff09; 思路&#xff1a; 要思考回溯怎么写&#xff08;入参与返回值、递归到哪里&#xff0c;递归的边界和入口&#xff09; DFS 滑行&#xff08;DFS 记忆搜索&#xff09; 代码分析&#xff1a; 学会将输入的数据用二维列表…...

[LeetCode][102]二叉树的层序遍历——遍历结果中每一层明显区分

题目 102. 二叉树的层序遍历 给定二叉树的根节点 root&#xff0c;返回节点值的层序遍历结果。即逐层地&#xff0c;从左到右访问所有节点。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[[3],[9,20],[15,7]] 示例 2&#xff1a; 输入…...

GIS之深度学习10:运行Faster RCNN算法

&#xff08;未完成&#xff0c;待补充&#xff09; 获取Faster RCNN源码 &#xff08;开源的很多&#xff0c;论文里也有&#xff0c;在这里不多赘述&#xff09; 替换自己的数据集&#xff08;图片标签文件&#xff09; &#xff08;需要使用labeling生成标签文件&#xf…...

appium2的一些配置

appium-desktop不再维护之后&#xff0c;需要使用appium2。 1、安装appium2 命令行输入npm i -g appium。安装之后输入appium或者appium-server即可启动appium 2、安装安卓/ios的驱动 安卓&#xff1a;appium driver install uiautomator2 iOS&#xff1a;appium driver i…...

基于springboot+vue实现高校学生党员发展管理系统项目【项目源码+论文说明】

基于springboot实现高校学生党员发展管理系统演示 摘要 随着高校学生规模的不断扩大&#xff0c;高校内的党员统计及发展管理工作面临较大的压力&#xff0c;高校信息化建设的不断优化发展也进一步促进了系统平台的应用&#xff0c;借助系统平台可以实现更加高效便捷的党员信息…...

Java代码审计安全篇-常见Java SQL注入

前言&#xff1a; 堕落了三个月&#xff0c;现在因为被找实习而困扰&#xff0c;着实自己能力不足&#xff0c;从今天开始 每天沉淀一点点 &#xff0c;准备秋招 加油 注意&#xff1a; 本文章参考qax的网络安全java代码审计&#xff0c;记录自己的学习过程&#xff0c;还希望…...

C#实现快速排序算法

C#实现快速排序算法 以下是C#中的快速排序算法实现示例&#xff1a; using System;class QuickSort {// 快速排序入口函数public static void Sort(int[] array){QuickSortRecursive(array, 0, array.Length - 1);}// 递归函数实现快速排序private static void QuickSortRecu…...

upload-labs通关记录

文章目录 前言 1.pass-012.pass-023.pass-034.pass-045.pass-056.pass-067.pass-078.pass-089.pass-0910.pass-1011.pass-1112.pass-1213.pass-1314.pass-1415.pass-1516.pass-1617.pass-1718.pass-1819.pass-19 前言 本篇文章记录upload-labs中&#xff0c;所有的通过技巧和各…...

Nginx实现高并发

注&#xff1a;文章是4年前在自己网站上写的&#xff0c;迁移过来了。现在看我之前写的这篇文章&#xff0c;描述得不是特别详细&#xff0c;但描述了Nginx的整体架构思想。如果对Nginx玩得透得或者想了解深入的&#xff0c;可以在网上找找其他的文章。 ......................…...

华为荣耀终端机试真题

文章目录 一 、字符展开(200分)1.1 题目描述1.2 解题思路1.3 解题代码二、共轭转置处理(100分)2.1 题目描述2.3 源码内容一 、字符展开(200分) 1.1 题目描述 // 64 位输出请用 printf(“%lld”)给定一个字符串,字符串包含数字、大小写字母以及括号(包括大括号、中括号…...

C++ Qt开发:QNetworkInterface网络接口组件

Qt 是一个跨平台C图形界面开发库&#xff0c;利用Qt可以快速开发跨平台窗体应用程序&#xff0c;在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置&#xff0c;实现图形化开发极大的方便了开发效率&#xff0c;本章将重点介绍如何运用QNetworkInterface组件实现查询详细的…...

Luajit 2023移动版本编译 v2.1.ROLLING

文章顶部有编好的 2.1.ROLLING 2023/08/21版本源码 Android 64 和 iOS 64 luajit 目前最新的源码tag版本为 v2.1.ROLLING on Aug 21, 2023应该是修正了很多bug, 我是出现下面问题才编的. cocos2dx-lua 游戏 黑屏 并报错: [LUA ERROR] bad light userdata pointer 编…...

c++ 常用新特性总结【c++11】,【c++14】,【c++17】,【c++20】

文章目录 常用的c11新特性1.自动推导类型(auto)2.lambda表达式3.智能指针4.范围for循环5.右值引用 - 移动语义6.类型别名7.constexpr8.static_assert(静态断言)9.nullptr10.列表初始化11.继承构造函数12.显示虚函数重载(override)13.final14.变长模板参数15.新的容器与算法16.强…...

服务器硬防的应用场景都有哪些?

服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式&#xff0c;避免服务器受到各种恶意攻击和网络威胁&#xff0c;那么&#xff0c;服务器硬防通常都会应用在哪些场景当中呢&#xff1f; 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

【生成模型】视频生成论文调研

工作清单 上游应用方向&#xff1a;控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...

iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈

在日常iOS开发过程中&#xff0c;性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期&#xff0c;开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发&#xff0c;但背后往往隐藏着系统资源调度不当…...

宇树科技,改名了!

提到国内具身智能和机器人领域的代表企业&#xff0c;那宇树科技&#xff08;Unitree&#xff09;必须名列其榜。 最近&#xff0c;宇树科技的一项新变动消息在业界引发了不少关注和讨论&#xff0c;即&#xff1a; 宇树向其合作伙伴发布了一封公司名称变更函称&#xff0c;因…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

Golang——7、包与接口详解

包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...

Unity中的transform.up

2025年6月8日&#xff0c;周日下午 在Unity中&#xff0c;transform.up是Transform组件的一个属性&#xff0c;表示游戏对象在世界空间中的“上”方向&#xff08;Y轴正方向&#xff09;&#xff0c;且会随对象旋转动态变化。以下是关键点解析&#xff1a; 基本定义 transfor…...

Axure 下拉框联动

实现选省、选完省之后选对应省份下的市区...

React从基础入门到高级实战:React 实战项目 - 项目五:微前端与模块化架构

React 实战项目&#xff1a;微前端与模块化架构 欢迎来到 React 开发教程专栏 的第 30 篇&#xff01;在前 29 篇文章中&#xff0c;我们从 React 的基础概念逐步深入到高级技巧&#xff0c;涵盖了组件设计、状态管理、路由配置、性能优化和企业级应用等核心内容。这一次&…...

基于单片机的宠物屋智能系统设计与实现(论文+源码)

本设计基于单片机的宠物屋智能系统核心是实现对宠物生活环境及状态的智能管理。系统以单片机为中枢&#xff0c;连接红外测温传感器&#xff0c;可实时精准捕捉宠物体温变化&#xff0c;以便及时发现健康异常&#xff1b;水位检测传感器时刻监测饮用水余量&#xff0c;防止宠物…...