事务隔离:从锁实现到MVCC实现

文章目录

  • 事务隔离:从锁实现到MVCC实现
    • 事务
      • 四大特性
      • 事务隔离级别
    • 锁实现
      • 概念
      • 实现事务隔离
    • MVCC实现
      • 当前读与快照读
      • 实现事务隔离
      • Read View
    • 总结

事务隔离:从锁实现到MVCC实现

面试的时候被面试官问到:你这个项目为什么使用了可重复读而不选择读已提交事务隔离级别。思考了一会发现我对事务、锁、事务隔离级别的理解还是有所欠缺,今天来整理一下。

本文的梳理都基于下面这张简单的表。

在这里插入图片描述

建表语句:

CREATE TABLE users (id INT AUTO_INCREMENT PRIMARY KEY,  -- 用户ID,自增主键username VARCHAR(50) NOT NULL,      -- 用户名,最多50字符,不能为空age INT,                            -- 年龄created_at DATETIME DEFAULT CURRENT_TIMESTAMP  -- 创建时间,默认当前时间
);

事务

事务就是数据库中的一系列操作组成的一个整体,我们在实际业务中,需要这一系列操作必须全部执行,不能有些操作执行了,有些操作执行失败了。

例如,小红向小明转账了500元,我们需要扣减小红500元的余额,增加小明500元的余额。

UPDATE users
SET balance = balance - 500
WHERE username = '小红';UPDATE users
SET balance = balance + 500
WHERE username = '小明';

如果不加事务控制,这两条语句可能会出现第一条执行了,第二条未执行的操作,就会导致500元不翼而飞了。

使用BEGIN开启一个事务,使用COMMIT提交一个事务。

START TRANSACTION;UPDATE users
SET balance = balance - 500
WHERE username = '小红';UPDATE users
SET balance = balance + 500
WHERE username = '小明';COMMIT;

对于单条sql语句,数据库会自动将其看成事务执行,叫隐式事务。

四大特性

事务的四大特性:

  • A:Atomicity,原子性,将所有SQL作为原子工作单元执行,要么全部执行,要么全部不执行;
  • C:Consistency,一致性,事务完成后,所有数据的状态都是一致的,即A账户只要减去了100,B账户则必定加上了100;
  • I:Isolation,隔离性,如果有多个事务并发执行,每个事务作出的修改必须与其他事务隔离;
  • D:Durability,持久性,即事务完成后,对数据库数据的修改被持久化存储。

我们所使用的锁、MVCC、日志等一系列机制其实都是为了保证事务的者四大特性,使得事务在实际业务中使用起来不会出错。

事务隔离级别

事务之间有四个隔离级别,分别是读未提交,读已提交(解决脏读),可重复读(解决不可重复读),串行化(解决幻读)。

锁实现

锁常用于解决并发请求导致的一系列问题。数据库锁也是用来处理当同时有多个事务或者请求来并发地访问数据库时,可能会出现的问题。

概念

数据库锁主要分为两种类型:

共享锁(Shared Lock): 也就是读锁,读锁和读锁不会互斥,读锁和写锁之间互斥。

排他锁(Exclusive Lock): 也就是写锁,写锁和任何读锁和写锁都是互斥的。

数据库中具体的锁:

由于本文只涉及到行锁,所以这里只简单介绍一下行锁。

行锁锁定数据库中的某一条记录,单个行。数据库不同行直接可以同时进行访问,因此提高了并发性。

共享行级锁:多个事务可以同时获取共享锁,用于读取行数据。

排他行级锁:只允许一个事务持有排他锁,用于修改行数据。

实现事务隔离

接下来我们梳理一下如何用锁来实现事务隔离级别。

首先我们要知道只依靠加锁来实现事务隔离会带来性能降低的问题,但是了解一下会对我们更好地去理解MVCC有帮助。

**一级封锁协议:**事务在修改一条记录之前必须先对它添加写锁,直到事务(提交或者回滚)结束才释放。

不能解决脏读问题,因为当事务T1修改记录,添加了写锁的时候,其它事务的读是不加锁的,依旧可以读到数据。

二级封锁协议: 一级封锁协议的规则+事务在读取记录之前必须先对它加读锁,读完后就可以释放锁。

可以解决脏读问题,但是不能解决不可重复读问题,因为读锁在读完就释放了,在到下一次读的这中间的间隙可能就会出现别的事务将数据修改了。

三级封锁协议: 一级封锁协议的规则+务在读取记录之前必须先对它加读锁,但是事务结束才释放。

可以解决脏读问题,可以解决不可重复读问题。

锁协议事务隔离级别
一级封锁协议Read Uncommitted(读未提交)
二级封锁协议Read Committed(读已提交)
三级封锁协议Repeatable Read(可重复读)
表锁Serializable (可串行化)

MVCC实现

可以看到上面我们只使用了锁机制来实现了事务隔离,接下来介绍一种无锁化的、性能更高的实现事务隔离的方法,MVCC(Multi-Version Concurrency Control),即多版本并发控制。

当前读与快照读

在了解MVCC实现事务隔离之前,我们先了解一下当前读和快照读。

当前读: Mysql使用锁来实现当前读,共享锁+排他锁+next-key lock(间隙锁)实现。

下面这些语法都是当前读:

语法
SELECT … LOCK IN SHARE MODE
SELECT … FOR UPDATE
UPDATE
DELETE
INSERT

select语句加上LOCK IN SHARE MODE就是加上共享锁进行读,加上FOR UPDATE就是加上排他锁进行读。

快照读: 快照读是在读取数据的时候读取一致性视图中的数据,Mysql使用MVCC来实现快照读。

具体而言,每个事务在开始时会创建一个一致性视图(Consistent View),该视图反映了事务开始时刻数据库的快照。这个一致性视图会记录当前事务开始时已经提交的数据版本。

当执行查询操作时,MySQL会根据事务的一致性视图来决定可见的数据版本。只有那些在事务开始之前已经提交的数据版本才是可见的,未提交的数据或在事务开始后修改的数据则对当前事务不可见。

实现事务隔离

那么MVCC在MySql中又是怎么样实现事务隔离的呢?

对于读未提交隔离级别,不做任何控制,相当于是一级封锁协议,修改语句会默认添加排他锁,并且在事务结束时才会释放。

对于读已提交隔离级别,MVCC通过Read View来实现。(具体看Read View)

对于可重复读隔离级别,MVCC通过Read View来实现。(具体看Read View)

对于串行化隔离级别,通过加临健锁(行锁+间隙锁)来实现的。

Read View

首先我们先要了解数据库的表记录,除了原来的数据列以外,还维护了3个隐藏列,和Read View相关的只有两个隐藏列,我们只关注这两个,一个是DB_TRX_ID,还有一个是DB_ROLL_PTR,其中DB_TRX_ID表明这条记录所属的事务id,DB_ROLL_PTR指向这条事务上一个事务所保存的这条记录的快照。所以对于一条记录,我们有一个多版本快照链(由DB_ROLL_PTR串联成链,读取选择读哪条的时候靠的是DB_TRX_ID来选择)。有了这个多版本快照链,事务在进行快照读的时候,就会结合Read View所记录的活跃的事务信息,选择当前隔离级别下可见的最新的记录了。

Read View就是mvcc实现快照读的核心机制,我们借助它就可以去undo_log中寻找要读的这条记录在当前事务隔离级别下"可见"的那个版本。下图就是一个Read View,它其实就是记录了事务的信息。

在这里插入图片描述

使用这个Read View的时候有一些规则:

(首先我们要知道每次读取记录的时候其实都是去当前这个记录的undo_log中去读取的,每次都先读取最新的版本,然后结合Read View进行判断,如果最新版本的不可读就会沿着版本链读取下一个版本直到可读。)

  1. 如果版本的记录的事务id是当前执行读取操作的事务id,则直接读取。

  2. 如果版本的记录的事务id小于Read View中的min_trx_id,那么表明该版本是在当前的读取事务开始之前就提交了的,可以读。

  3. 如果版本的记录的事务id大于Read View中保存的max_trx_id,那么表明该版本是在当前读取操作的事务开始之后提交的,不能读取。

  4. 如果当前读取操作的事务id位于Read View中记录的min_trx_id到max_trx_id之间,那么就表明这个版本是在当前活跃的但是还未提交的版本中的,那么只要在读已提交版本之上,就也不可以读。

读已提交可重复读 有一个很大的区别就是读已提交在每次读取操作的时候都会创建一个新的当前状态的Read View,而可重复读只会再事务第一次读取操作之后创建一个Read View,并且使用这个Read View一直到事务结束。就是这个区别使得 读已提交 可能导致 不可重复读 的问题。因为第二次读取使用的是一个更新后的新 Read View,可能读到了其他事务刚刚提交的新值。

总结

因此,有了MVCC,有了Read View,我们可以无锁地实现事务隔离级别,在读取操作地时候不上锁(没有MVCC的话,读取的时候要使用共享锁来进行控制),只有在修改操作的时候正常加上排他锁,大大地提高了并发事务的性能。

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

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

相关文章

小架构step系列18:工具

1 概述 在写代码的时候,有很多通用的、与业务无关逻辑,这些一般写成工具类方法。这些工具类方法慢慢地被积累起来,变成了开源包,可以直接使用开源包,而不是自己再花时间来重复造这些轮子。 这些工具类的开源包比较多…

网络、CentOS 系统、数据库面试知识点总结

文章目录Linux CentOS 面试知识点整理速查复习✅ 一、Linux 高频面试题✅ 二、MySQL 高频面试题✅ 三、计算机网络(OSI四层模型)高频面试题🔗 链路层(Link Layer)🌐 网络层(Internet Layer&…

Vue (Official) v3.0.2 新特性 为非类npm环境引入 globalTypesPath 选项

目录 前言 报错信息 原因 解决方案 总结 前言 在早上更新了vscode后,发现自己 uni-app 项目的 .vue文件 的 template 标签都出现了报错。定位到了问题是因为 Vue (Official) 插件更新导致的,重装了插件的上一个小版本,报错消失&#xff…

程序可能的输出

#include "csapp.h"int main() {int x 3;if (Fork() ! 0)printf("x%d\n", x);printf("x%d\n", --x);exit(0); }分析:父进程先执行printf("x%d\n", x); 输出x4。后执行 printf("x%d\n", --x);输出x3。子进程只执…

2025年UDP应用抗洪指南:从T级清洗到AI免疫,实战防御UDP洪水攻击

一次未防护的UDP暴露,可能让日活百万的应用瞬间瘫痪,损失超千万2025年,随着物联网僵尸网络规模指数级增长及AI驱动的自适应攻击工具泛滥,UDP洪水攻击峰值已突破8Tbps,单次攻击成本却降至50元以下。更致命的是&#xff…

centos7安装MySQL8.4手册

目录前言一、首先更新插件,并查看当前系统版本二、安装步骤1、创建mysql目录2、安装rpm包3、安装 mysql-community-server4、启动MySQL服务5、查看MySQL状态6、设置开机自启动三、查看默认密码四、登录mysql五、修改密码六、开启远程访问1. 修改 MySQL 配置文件2. 重…

人脸检测算法——SCRFD

SCRFD算法核心解析 1. 算法定义与背景 SCRFD(Sample and Computation Redistribution for Efficient Face Detection)由Jia Guo等人于2021年在arXiv提出,是一种高效、高精度的人脸检测算法,其核心创新在于: 双重重分…

vue3+ts+elementui-表格根据相同值合并

代码<div style"height: auto; overflow: auto"><el-table ref"dataTableRef" v-loading"loading" :data"pageData" highlight-current-row borderselection-change"handleSelectionChange" :span-method"obj…

UI前端与数字孪生融合案例:智慧城市的智慧停车引导系统

hello宝子们...我们是艾斯视觉擅长ui设计、前端开发、数字孪生、大数据、三维建模、三维动画10年经验!希望我的分享能帮助到您!如需帮助可以评论关注私信我们一起探讨!致敬感谢感恩!一、引言&#xff1a;停车难的 “城市痛点” 与数字孪生的破局之道当司机在商圈绕圈 30 分钟仍…

java+vue+SpringBoot集团门户网站(程序+数据库+报告+部署教程+答辩指导)

源代码数据库LW文档&#xff08;1万字以上&#xff09;开题报告答辩稿ppt部署教程代码讲解代码时间修改工具 技术实现 开发语言&#xff1a;后端&#xff1a;Java 前端&#xff1a;vue框架&#xff1a;springboot数据库&#xff1a;mysql 开发工具 JDK版本&#xff1a;JDK1.8 数…

【Docker基础】Docker-compose从入门到精通:安装指南与核心命令全解析

目录 前言 1 Docker-compose核心概念解析 1.1 什么是Docker-compose&#xff1f; 1.2 典型应用场景 2 Docker-compose离线安装详解 2.1 离线安装背景与优势 2.2 详细安装步骤 步骤1&#xff1a;获取离线安装包 步骤2&#xff1a;文件部署与权限设置 步骤3&#xff1a…

面试150 被围绕的区域

思路 使用DFS&#xff0c;将所有与边界相连的’O’都修改为‘#’,然后遍历数组&#xff0c;如果是遇到’#‘修改为’O’,如果是’O’修改为’X’。 class Solution:def solve(self, board: List[List[str]]) -> None:"""Do not return anything, modify boar…

(数据结构)线性表(上):SeqList 顺序表

线性表&#xff08;上&#xff09;&#xff1a;Seqlist 顺序表基本了解线性表顺序表静态顺序表动态顺序表编写动态顺序表项目结构基础结构初始化尾插头插尾删头删查找指定位置pos之前插入数据删除指定位置pos的数据销毁完整代码SeqLIst.hSeqLIst.ctest.c算法题移除元素删除有序…

WebStorm vs VSCode:前端圈的「豆腐脑甜咸之争」

目录 一、初识两位主角&#xff1a;老司机与新势力 二、开箱体验&#xff1a;是「拎包入住」还是「毛坯房改造」 三、智能提示&#xff1a;是「知心秘书」还是「百度搜索」 四、调试功能&#xff1a;是「CT 扫描仪」还是「听诊器」 五、性能表现&#xff1a;是「重型坦克」…

C#将类属性保存到Ini文件方法(利用拓展方法,反射方式获取到分组名和属性名称属性值)

前言&#xff1a;最近学习C#高级课程&#xff0c;里面学到了利用反射和可以得到属性的特性、属性名、属性值&#xff0c;还有拓展方法&#xff0c;一直想将学到的东西利用起来&#xff0c;刚好今天在研究PropertyGrid控件时&#xff0c;想方便一点保存属性值到配置文件&#xf…

kafka 单机部署指南(KRaft 版本)

目录环境准备JDK安装下载jdkjdk安装kafka 部署kafka 下载kafka 版本号结构解析kafka 安装下载和解压安装包配置 KRaft 模式格式化存储目录启动kafkaKafka 配置为 systemd 服务注意事项调整 JVM 内存参数Kafka KRaft 版本&#xff08;即 Kafka 3.0 及更高版本&#xff09;使用 K…

websocket案例 599足球比分

目标地址:aHR0cHM6Ly93d3cuNTk5LmNvbS9saXZlLw接口:打开控制台 点websocket 刷新页面 显示分析:不写理论了关于websocket 几乎发包位置都是下方图片 不管抖音还是快手 等平台这里在进行 new WebSocket 后 是要必须走一步的 也就是 new WebSocket().onopen() 也就是onopen 进行向…

【后端】Linux系统发布.NetCore项目

目录 1.设置全球化不变模式 1.发布到文件 3. 配置为服务 3.1.添加服务 3.2.添加执行权限 3.3.启动服务 4.访问 1.设置全球化不变模式 双击所需项目&#xff0c;设置全球化不变模式 <!-- 设置全球化不变模式 --><RuntimeHostConfigurationOption>System.Globa…

CMU-15445(2024fall)——PROJECT#0

题目介绍 这是题目原文。 注意&#xff1a;在拉取项目的时候需要注意拉取2024fall的Tag&#xff0c;本人血泪教训&#xff01; 本题是关于HyperLogLog的具体实现&#xff0c;其介绍可以看这个视频进行了解。简单来说HyperLogLog可以在一个非常小的固定内存下&#xff08;一般…

使用微信免费的图像处理接口,来开发图片智能裁剪和二维码/条码识别功能,爽歪歪

大家好&#xff0c;我是小悟。 1、图片智能裁剪 我们先来了解一下图片智能裁剪。图片智能裁剪聚焦于数字图像的智能化处理。AI技术的引入彻底改变了传统依赖人工框选的裁剪模式。 通过深度学习模型自动识别图像主体&#xff08;人物、商品等&#xff09;&#xff0c;能在极短时…