【MySQL】可重复读级别下基于Next Key Lock解决幻读

昨天读到了一篇文章[1],里面讲,面试官说mysql的可重复读级别下有解决幻读的方式,最后公布了答案,是在sql后面加for update。这么说倒是没错,但是这种问法给我一种奇怪的感觉,因为for update无论在哪个隔离级别下,都能提供x锁进行锁定防止幻读,而next key lock,是x锁的三种实现算法之一,next key lock,也不应该翻译为间隙锁。下面我们一一讲来。

一,mysql的锁

mysql的锁,

按照读或写,分成s锁和x锁

按照锁的范围,分成表锁和行锁

按照意向锁的读或写,分成is锁和ix锁。其中is锁和ix锁是表锁,但不是真的用排他锁锁表,而是一种共享锁,旨在告诉其他线程表中有s锁或者x锁;而s锁和x锁是真的加了排他锁,s锁是对读共享对写排他,x锁是对其他线程的任何操作都排他,且s锁和x锁都是行锁。

而最后一个,mysql行锁的三种算法。

行锁(Record Lock),只锁单行记录本身。

间隙锁(Gap Lock),锁住记录两边的记录,但是对记录本身,不加锁。

临键锁(Next Key Lock),即锁住记录本身,也锁住记录两边,这种锁的范围是左闭右开。还有一种锁,叫做Previous Key Lock,和Next Key Lock类似,区别只是锁的范围左开右闭。它们都来自于谓词锁,即锁住满足某一查询条件的所有数据项,它不仅包括当前在数据库中满足条件的记录,也包括即将要插入,更新或删除到数据库并满足查询条件的数据项。

下图来自《MySQL技术内幕 InnoDB存储引擎》一书。
在这里插入图片描述
在这里插入图片描述

第一个需要清晰指出的误区是,Next Key Lock翻译为临键锁[2],而不是间隙锁。next意为下一个,key意为键;而gap指空挡、间隙,因此Gap Lock才是间隙锁。

第二个误区是,Next Key Lock确实能够解决幻读问题,但是不止在可重复读(rr)级别下,你在所有级别下,在sql后面加for update都能施加x锁(有时是临键锁有时是行锁,这个后面讲)解决幻读问题。

二,mysql解决并发问题的机制

mysql有两种解决并发问题的机制,一种叫做基于一致性的非锁定读(Multi Version Concurrent Control, MVCC),也叫快照读;另一种叫做基于一致性的锁定读(Lock Based Concurrent Control, LBCC)也叫当前读

mysql四种隔离级别,读未提交,就是没有并发控制;读已提交和可重复读默认走的MVCC;串行化走的LBCC;但是不管哪个级别,加了lock in share mode(s锁)和for update(x锁)都是走的LBCC了。

简单来说,读已提交和可重复读不加锁走MVCC,加锁走LBCC。串行化就走LBCC没法走MVCC;读未提交默认没并发控制,加了所走LBCC,不过这种级别应该也应用不广。

MVCC其实就是不直接读取记录本身,只读写操作形成的undo log,undo log其实就是一种数据快照,其中包括记录所有数据,此外还多了一个指向其他undo log的指针(roll_ptr)和生成此undo log的事务id(trix_id),因此MVCC被称为快照读。这些不同事务写操作的undo log形成一个undo log链表,在查询的时候事务去undo log链表中进行查询,读已提交和可重复读的区别就是,读已提交每次查询都生成一个ReadView,而可重复读只有第一次查询生成一个ReadView。ReadView如何控制查询和解决不可重复读问题的,可以看往期文章[3]。

LBCC是直接去读取记录本身,这个没有什么好说的。

需要特别说明的是,MVCC和LBCC都是针对读请求的并发机制,对于写请求,会直接对要修改的行加锁进行操作,写操作在事务中会形成undo log并不持久化到硬盘,事务提交后持久化到硬盘并删除无用的undo log。

三,Next Key Lock如何解决幻读问题

要知道Next Key Lock如何解决幻读问题,首先要知道什么是幻读。幻读问题,简单的说就是a事务进行了两次相同的查询,第二次查询结果比第一次多了一些结果,原因是b事务在a事务两次查询中间,向其查询范围内插入了数据并提交,导致a事务第二次查询范围内多出了这些b事务提交的数据。好,接下来我们看一下例子。

假设有这样的一个sql,其中a是辅助索引,a的值有1、3、5、8、10。

select * from t where a = 5 for update;

由于加了x锁,走的LBCC,x锁的具体实现为临键锁,锁住(3, 5)+5+(5,8),即(3,8)。为什么使用临键锁锁住一整个区域呢,用行锁Record Lock只锁住一行不行吗,毕竟我们的查询条件只是a=5。

答案是不行。行锁的定义就是只锁住一行,而且a是辅助索引,并非唯一索引,意味着a=5可能有多行,只靠Record Lock锁不住多行a=5的记录。不仅如此,还有可能新插入的a=5的记录插入在原本a=5索引的最左侧或最右侧,因此要使用Next Key Lock锁住接近a=5的所有索引值对应的记录。即(3, 5)范围内有可能插入一个新的a=5的记录,(5,8)范围内也有可能插入一个新的a=5的记录,因此要锁住临近a=5的一整个范围。整个可能被插入的范围都被锁住,那么新插入数据的可能性就不存在了,幻读因此不可能出现。

至此,我们解释了为什么不能直接使用行锁来加锁,也解释了临键锁为什么能防止幻读。

四,Next Key Lock降级为Record Lock

前面我们解释了为什么普通索引要使用临键锁。其实还有一种情况,即等值查询时,使用的索引是唯一索引,那么Next Key Lock会被mysql降级为Record Lock。

因为唯一索引的数据是唯一的,不管查找的唯一索引的数据有或者没有,都只需要锁住一行即可,使用行锁锁住一行就能锁住等值查询的要找的那一条记录。

五,没有LBCC,可重复读只靠默认的MVCC能解决幻读问题吗

在可重复读级别下,只靠MVCC能够防止幻读吗?先说答案,部分情况下能,部分情况下,不能。

1,能防止幻读的情况

先说一下能防止幻读的原因。a事务进行了两次相同的查询,b事务在这两次查询中间,插入了在a事务查询范围内的数据,并提交,导致a事务第二次查询比第一次查询多出了数据,这就是幻读,两次相同的查询但是结果不一样,多了数据。

出现这种幻读的情况下,分为两种情况,事务b比事务a早开启,和事务b比事务a晚开启。

(1)假设事务b比事务a早开启,在事务a两次查询中间提交。

那么事务a第一次查询时生成了一个ReadView,在事务a第二次查询时,还会使用这个ReadView去进行查询(因为是可重复读级别下)。首先事务a会发现根据这个ReadView,事务b的trx_id在ReadView的min_trx_id和max_trx_id之间,表示事务b可能是活跃的事务,是不是还需要查看m_ids列表;

第二步,其中的m_ids列表(包括了生成ReadView时所有活跃的事务id)会包含事务b的trx_id,因此会认为事务b还是一个活跃事务,活跃事务的数据属于脏数据,因此不会被事务a读取。

注,ReadView的使用规则,查看文章[3]。

(2),假设事务b比事务a晚开启,在事务a两次查询中间提交。则事务b在提交数据后生成的undo log的trx_id就是事务b的trx_id,事务b要比事务a晚开启,意味着事务b的trx_id比事务a第一次查询时生成的ReadView的max_trx_id要大,则事务a在第二次查询时,可以通过ReadView知道,事务b是生成ReadView以后才开启,则事务a不会读取将来的数据。

两种情况列举下来,均可以避免幻读。

2,不能防止幻读的情况

那么什么时候不能防止幻读呢?在防止幻读时举的例子如下:
a事务进行了两次相同的查询,b事务在这两次查询中间,插入了在a事务查询范围内的数据,并提交,导致a事务第二次查询比第一次查询多出了数据

我们只需要在事务a第二次查询数据前,对事务b提交的数据做修改,然后再进行事务a的第二次查询。只用添加这么一步,就会出现幻读。

这是因为update本身不会根据MVCC,具体说就是ReadView去查看是否可以修改,update本身是加悲观锁直接对记录进行修改的,也就是说在事务b插入并提交以后,事务a是可以找到这条数据并成功修改的,并因此生成了undo log,并且undo log的trx_id就是事务a自己。

此时事务a再进行第二次查询,事务a查到符合范围的数据中,有一个自己修改的undo log快照,符合条件,于是就作为结果展示出来了,因此产生了幻读。

以上,就是关于可重复读、MVCC、LBCC、临键锁、间隙锁的一些解释。

参考文章:
[1],Mysql 间隙锁原理,以及Repeatable Read隔离级别下可以防止幻读原理(百度)
[2],详解 MySql InnoDB中的三种行锁(记录锁、间隙锁与临键锁)
[3],【MySQL】基于MVCC的RR、RC级别事务原理简述

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

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

相关文章

Mybatis学习笔记(三)

十、MyBatis的逆向工程 (一)逆向工程介绍 MyBatis的一个主要的特点就是需要程序员自己编写sql,那么如果表太多的话,难免会很麻烦,所以mybatis官方提供了一个逆向工程,可以针对单表自动生成mybatis执行所需要的代码(包…

dns构建

(1)用户输入域名发起域名查询请求。 (2)计算机操作系统先查找本地hosts文件中是否有这个域名与IP的对应关系,有就返回结果给用户,没有就进入下一步。 (3)hosts文件找那个没有此域名…

<项目代码>YOLOv8 pcb板缺陷检测<目标检测>

YOLOv8是一种单阶段(one-stage)检测算法,它将目标检测问题转化为一个回归问题,能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法(如Faster R-CNN),YOLOv8具有更高的…

FET113i-S核心板已支持RISC-V,打造国产化降本的更优解 -飞凌嵌入式

FET113i-S核心板是飞凌嵌入式基于全志T113-i处理器设计的国产工业级核心板,凭借卓越的稳定性和超高性价比,FET113i-S核心板得到了客户朋友们的广泛关注。作为一款拥有A7核RISC-V核DSP核的多核异构架构芯片,全志科技于近期释放了T113-i的RISC-…

打印速度与精度难兼顾,动态界面打印能否破解?

大家好!在科技飞速发展的今天,3D 打印技术已深入众多领域。然而,传统打印技术面临着速度、材料、精度等诸多挑战。在此背景下,一种名为动态界面打印(DIP)的新技术应运而生——《Dynamic interface printing…

IMS 注册慢问题分析

1、问题描述 VOLTE长时间没注册上。 2、Log分析 10-04 15:49:04.745089 2118 3531 D ImsService: enableIms, phoneId 1 10-04 15:49:04.757739 1423 1498 D RmcImsCtlReqHdl: [1] requestSetImsCfg volte:1, vilte:1, vowifi:0, viwifi:0, sms:1, imsTe…

【网络】套接字编程——TCP通信

> 作者:დ旧言~ > 座右铭:松树千年终是朽,槿花一日自为荣。 > 目标:TCP网络服务器简单模拟实现。 > 毒鸡汤:有些事情,总是不明白,所以我不会坚持。早安! > 专栏选自:…

Django Form 实现多层(嵌套)模型表单

在 Django 中,可以通过使用 ModelForm 和 InlineFormSet 来实现多层(嵌套)模型表单。这样可以在一个表单中同时编辑主模型及其相关的子模型。下面是一个示例,演示如何实现这种多层嵌套的表单。 1、问题背景 如何使用 Django 的 F…

Linux - grep的正则用法

新建u.txt,文本内容如图: 搜寻特定字符串 利用中括号[]搜寻集合字符 行首与行位字符^$ 任意一个字符.与重复字符*限定连续RE字符范围{} 总结:

落地灯选什么光源最好?五款值得入手的大路灯护眼灯推荐

落地灯选什么光源最好?落地灯是既适合日常人群使用,又适合长时间用眼的学生党、码字党使用的一种照明神器,因此热度一直都很高。但是该行业内的产品也很复杂,其中还有一些劣质不专业的产品掺杂在其中,不但灯光效果不明…

如何获取免费的纯真社区版IP库授权?

纯真社区版IP库 1、访问官网 https://cz88.net/geo-public 地址注册账号 2、登录账号后,申请api 授权

【AAOS】【源码分析】CarSystemUI -- CarSystemBar

CarSystemBar不像Android手机那样固定的顶部“状态栏”和底部“导航栏”,而是将StatusBar和NavigationBar都统称为SystemBar,可以通过如下配置为每侧最多配置一个“系统栏”。 packages/apps/Car/SystemUI/res/values/config.xml<!-- Configure which system bars should …

【DBeaver】连接带kerberos的hive[Apache|HDP]

目录 一、安装配置Kerberos客户端环境 1.1 安装Kerberos客户端 1.2 环境配置 二、基于Cloudera驱动创建连接 三、基于Hive原生驱动创建连接 一、安装配置Kerberos客户端环境 1.1 安装Kerberos客户端 在Kerberos官网下载,地址如下&#xff1a;https://web.mit.edu/kerberos…

1.探索WebSocket:实时网络的心跳!

序言 你可能听说过"WebSokcet"这个词&#xff0c;感觉它好像很高深&#xff0c;但其实它是一个超级酷的小工具&#xff0c;让我们在Web应用里实现实时通信。想象一下&#xff0c;你可以像聊天一样&#xff0c;在浏览器和服务器之间来回“畅聊“&#xff0c;没有延迟…

【大数据学习 | kafka】kafka的数据存储结构

以上是kafka的数据的存储方式。 这些数据可以在服务器集群上对应的文件夹中查看到。 [hexuanhadoop106 __consumer_offsets-0]$ ll 总用量 8 -rw-rw-r--. 1 hexuan hexuan 10485760 10月 28 22:21 00000000000000000000.index -rw-rw-r--. 1 hexuan hexuan 0 10月 28 …

做一个干电池的电量检测器03:数值拟合与电路仿真

首先在表格中进行详细的计算&#xff0c;整理出所需的数据。接着&#xff0c;我们运用MATLAB的强大功能对这些数据进行插值处理&#xff0c;生成了一个离散的数值数组。这个数组的每个数值都精确地对应着模数转换器&#xff08;ADC&#xff09;采样到的信号。通过这些数值&…

MFC工控项目实例二十八模拟量信号每秒采集100次

用两个多媒体定时器&#xff0c;一个定时0.1秒计时&#xff0c;另一个定时0.01秒用来对模拟量信号采集每秒100次。 1、在SEAL_PRESSUREDlg.h中添加代码 class CSEAL_PRESSUREDlg : public CDialo { public:CSEAL_PRESSUREDlg(CWnd* pParent NULL); // standard constructor&a…

【Mac】Screen Recorder by Omi Mac:Omi录屏专家

大家好&#xff0c;今天给大家介绍的这款软件叫Screen Recorder by Omi Mac&#xff1a;Omi录屏专家。 软件介绍 OmniRecorder for Mac 是一款用于录制屏幕的应用程序&#xff0c;专为 macOS 设计。它允许用户录制整个屏幕或特定区域&#xff0c;支持音频录制和实时编辑。这个…

多波束T50P和SES2000 Medium100安装记录(2024年10月)

SES2000 Medium100买了一直没有机会用&#xff0c;本次外业刚好需要。SES2000最大穿透70m。 Medium100安装与SES2000 Standard基本相同。除了钢管和法兰不同以外&#xff0c;它们安装支架都可以通用。有条件的话&#xff0c;用焊接方式将其固定在船侧舷&#xff0c;前方加一道拉…

Nginx安装配置详解

Nginx Nginx官网 Tengine翻译的Nginx中文文档 轻量级的Web服务器&#xff0c;主要有反向代理、负载均衡的功能。 能够支撑5万的并发量&#xff0c;运行时内存和CPU占用低&#xff0c;配置简单&#xff0c;运行稳定。 写在前 uWSGI与Nginx的关系 1. 安装 Windows 官网 Stabl…