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种,由低到高分别为Read uncommitted (读未提交)、Read committed(读提交) 、Repeatable read(可重复读) 、Serializable (串行化&a…...

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

Igraph入门指南 4
二、图的创建 图分有向图和无向图,所以图的创建有各自的实现方式。 1、手工创建图: 1-1 通过文本创建:graph_from_literal 通过每项提供两个顶点名(或ID号)作为一条边的格式,手动创建图,顶点…...

外包干了30天,技术明显退步。。
🍅 视频学习:文末有免费的配套视频可观看 🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 这次来聊一个大家可能也比较关心的问题,那就是就业城市选择的问题。而谈到这个问题&a…...

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

eclipse搭建java web项目
准备条件 eclipsejdk1.8 (配置jdk环境)apache-tomcat-8.5.97(记住安装位置) 一 点击完成 开始创建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 在文件中指定构建,测试和部署脚本 3、gitlab将检测到他并使用名为git Runner的工具运行脚本 4、脚本被分组为作业,他们共同组成了一个管道gitlab-ci的脚本执行,需要自…...

python 蓝桥杯之动态规划入门
文章目录 DFS滑行(DFS 记忆搜索) 思路: 要思考回溯怎么写(入参与返回值、递归到哪里,递归的边界和入口) DFS 滑行(DFS 记忆搜索) 代码分析: 学会将输入的数据用二维列表…...

[LeetCode][102]二叉树的层序遍历——遍历结果中每一层明显区分
题目 102. 二叉树的层序遍历 给定二叉树的根节点 root,返回节点值的层序遍历结果。即逐层地,从左到右访问所有节点。 示例 1: 输入:root [3,9,20,null,null,15,7] 输出:[[3],[9,20],[15,7]] 示例 2: 输入…...

GIS之深度学习10:运行Faster RCNN算法
(未完成,待补充) 获取Faster RCNN源码 (开源的很多,论文里也有,在这里不多赘述) 替换自己的数据集(图片标签文件) (需要使用labeling生成标签文件…...

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

基于springboot+vue实现高校学生党员发展管理系统项目【项目源码+论文说明】
基于springboot实现高校学生党员发展管理系统演示 摘要 随着高校学生规模的不断扩大,高校内的党员统计及发展管理工作面临较大的压力,高校信息化建设的不断优化发展也进一步促进了系统平台的应用,借助系统平台可以实现更加高效便捷的党员信息…...

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

C#实现快速排序算法
C#实现快速排序算法 以下是C#中的快速排序算法实现示例: 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中,所有的通过技巧和各…...

Nginx实现高并发
注:文章是4年前在自己网站上写的,迁移过来了。现在看我之前写的这篇文章,描述得不是特别详细,但描述了Nginx的整体架构思想。如果对Nginx玩得透得或者想了解深入的,可以在网上找找其他的文章。 ......................…...
华为荣耀终端机试真题
文章目录 一 、字符展开(200分)1.1 题目描述1.2 解题思路1.3 解题代码二、共轭转置处理(100分)2.1 题目描述2.3 源码内容一 、字符展开(200分) 1.1 题目描述 // 64 位输出请用 printf(“%lld”)给定一个字符串,字符串包含数字、大小写字母以及括号(包括大括号、中括号…...

C++ Qt开发:QNetworkInterface网络接口组件
Qt 是一个跨平台C图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍如何运用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.强…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...
快刀集(1): 一刀斩断视频片头广告
一刀流:用一个简单脚本,秒杀视频片头广告,还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农,平时写代码之余看看电影、补补片,是再正常不过的事。 电影嘛,要沉浸,…...

R 语言科研绘图第 55 期 --- 网络图-聚类
在发表科研论文的过程中,科研绘图是必不可少的,一张好看的图形会是文章很大的加分项。 为了便于使用,本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中,获取方式: R 语言科研绘图模板 --- sciRplothttps://mp.…...

【C++】纯虚函数类外可以写实现吗?
1. 答案 先说答案,可以。 2.代码测试 .h头文件 #include <iostream> #include <string>// 抽象基类 class AbstractBase { public:AbstractBase() default;virtual ~AbstractBase() default; // 默认析构函数public:virtual int PureVirtualFunct…...
华为OD最新机试真题-数组组成的最小数字-OD统一考试(B卷)
题目描述 给定一个整型数组,请从该数组中选择3个元素 组成最小数字并输出 (如果数组长度小于3,则选择数组中所有元素来组成最小数字)。 输入描述 行用半角逗号分割的字符串记录的整型数组,0<数组长度<= 100,0<整数的取值范围<= 10000。 输出描述 由3个元素组成…...

新版NANO下载烧录过程
一、序言 搭建 Jetson 系列产品烧录系统的环境需要在电脑主机上安装 Ubuntu 系统。此处使用 18.04 LTS。 二、环境搭建 1、安装库 $ sudo apt-get install qemu-user-static$ sudo apt-get install python 搭建环境的过程需要这个应用库来将某些 NVIDIA 软件组件安装到 Je…...

Android Framework预装traceroute执行文件到system/bin下
文章目录 Android SDK中寻找traceroute代码内置traceroute到SDK中traceroute参数说明-I 参数(使用 ICMP Echo 请求)-T 参数(使用 TCP SYN 包) 相关文章 Android SDK中寻找traceroute代码 设备使用的是Android 11,在/s…...
2025年全国I卷数学压轴题解答
第19题第3问: b b b 使得存在 t t t, 对于任意的 x x x, 5 cos x − cos ( 5 x t ) < b 5\cos x-\cos(5xt)<b 5cosx−cos(5xt)<b, 求 b b b 的最小值. 解: b b b 的最小值 b m i n min t max x g ( x , t ) b_{min}\min_{t} \max_{x} g(x,t) bmi…...