我打赌!这个 SQL 题,大部分人答不出来

数据库2025-11-05 06:29:395

大家好,打赌我是部分不出小林。

周末的人答时候,一个读者问了我一个很有意思的打赌问题,是部分不出关于 MySQL 中 update 加锁的问题。

他用下面这张数据库表,人答做了个 MySQL 实验的打赌时候。

发现事务 B 的部分不出 update 不会阻塞,而事务 C 的人答 update 会阻塞,都是打赌对 id = 10 这条记录进行 update, 为什么一个会阻塞,部分不出一个不会阻塞?人答

首先,我们先来分析下,打赌事务 A 这条 SQL 加了什么锁。部分不出

复制// 事务 A

select * from t_person where id < 10 for update;1.2.

我直接说结论,人答事务 A  加了这三个行级锁:

在 id 为 1 的主键索引上,加了 X 型的 next-key 锁,范围是 (-∞,1]。意味着,其他事务无法对 id = 1 的记录进行删除和更新操作,同时无法插入 id 小于 1 的服务器租用新记录。在 id 为 5 的主键索引上,加了 X 型的 next-key 锁,范围是 (1, 5]。意味着,其他事务无法对 id = 5 的记录进行删除和更新操作,同时无法插入 id 为 2、3、4 的新记录。在 id 为 10 的主键索引上,加了 X 型的间隙锁,范围是 (5, 10)。意味着,其他事务无法插入 id 为 6、7、8、9 的新纪录。

PS:如果你不清楚什么是 MySQL 这些行级锁(记录锁、间隙锁、next-key 锁),以及不清楚行级锁的加锁规则,强烈建议先看我之前写的云服务器这篇:​​MySQL 是怎么加行级锁的?​​,看完后,你回头看我这篇文章,就会有感觉的了。

事务 B 的 update 语句为什么不会阻塞?

事务 B 的 update 语句是对 id = 10 的行记录的 name 字段进行更新。

复制//

事务 B

update t_person set name = "小林" where id = 10;1.2.

事务 B 会在 id = 10 的主键索引上加 X 型记录锁,仅锁住这一行。因为当我们用唯一索引进行等值查询的时候,查询的记录是「存在」的,在索引树上定位到这一条记录后,该记录的索引中的 next-key 锁会退化成「记录锁」。

事务 A 并没有对 id = 10 的主键索引上加 X 型记录锁,而是对 id = 10 的主键索引上加 X 型间隙锁。间隙锁和记录锁之间是没有互斥关系的企商汇,所以事务 B 的 update 语句不会阻塞。

事务 C 的 update 语句为什么会阻塞?

事务 C 的 update 语句是将 id = 10 的行记录的 id 更新为 2。

复制//

事务 C

update t_person set id = 2 where id = 10;1.2.

这条 update 很特殊,特殊之处在于更新了主键索引。你以为它只是一个更新操作,实际上它在背后执行了两个操作:

操作 1:delete from t_person where id = 10;操作 2:insert into t_person (2, 陈某,  30, 广州市海珠区);

也就是先删除 id = 10 的记录,然后再插入 id = 2 的新纪录。

为什么当 update 语句更新了索引值,会被拆分成删除和插入操作?

要回答这个问题,我们先要清楚 B+ 树的特点。

Innodb(MySQL 存储引擎)在实现索引的时候,采用的数据结构是 B+ 树。B+ 树是基于二分查找树演变过来的,所以 B+ 树在存储索引的时候,是按顺序存储的,因为这样才能利用二分查找快速检索到索引。

现在有一颗这样的  B+ 树,可以看到叶子节点的索引值是从小到大的顺序。

假设这时候需要将索引值为 25 更新为 3,如果直接索引值为 25 的位置上,将值改为 3 的话。

这时候你就会发现这棵 B+ 树不满足顺序性了!

所以更新索引的值,不能只是修改一个索引值就完事,而是还要保证更新后的索引值能继续满足  B+ 树的顺序性。

解决的方法就是,先删除索引值为 25 的节点,再插入索引值为 3 的节点,这样,这颗 B+ 树才能满足顺序性。

事务 C 的 update  语句具体阻塞在哪个「操作」?

现在我们知道,事务 C 的 update 特殊语句背后执行了两个操作,分别是删除和插入操作,那具体是阻塞在哪个「操作 」?

「操作 1 」是删除 id = 10 的记录,事务 C 是会在 id = 10 的主键索引上加 X 型记录锁,而事务 A 并没有对 id = 10 的主键索引上加 X 型记录锁,而是对 id = 10 的主键索引上加 X 型间隙锁。间隙锁和记录锁之间是没有互斥关系的,所以「操作 1 」不会阻塞。

根据排除法,既然 「操作 1 」不会阻塞,那事务 C 的 update 语句阻塞的原因就是因为 「操作 2」发生了阻塞。

为什么「操作2」会发生阻塞呢?

我们先要知道,插入操作什么时候会发生阻塞:插入语句在插入一条新记录之前,需要先定位到该记录在 B+树的位置,如果插入的位置的下一条记录的索引上有间隙锁,此时会生成一个插入意向锁,然后锁的状态设置为等待状态,现象就是插入语句会被阻塞。

「操作 2」插入的是 id = 2 的新记录,在主键索引的 B+树定位到插入的位置如下图。

插入位置的下一条记录是 id = 5 的记录,而事务 A 在 id 为 5 的主键索引上已经加了 X 型的 next-key 锁,这里面包含了间隙锁。所以「操作 2」的插入操作会发生阻塞,这就是事务 C 的 update 语句阻塞的原因。

从这我们也可以知道间隙锁的作用,就是阻止其他事务在间隙锁的范围内插入新记录,从而避免可重复读隔离级别下幻读的现象。

我们也可以通过 select * from performance_schema.data_locks\G; 这条语句,查看事务 C 在加什么锁的时候导致阻塞。

从上面的输出信息,可以看到事务 C 在加「插入意向锁」的时候,发生了阻塞。

插入意向锁是插入操作才会有的锁,而事务 C 只是执行 update 语句,却出现了插入意向锁,从这里也可以证明,事务 C 这条特殊的 update 语句运行的时候,被拆分成了两个操作,一个是删除,另一个是插入。

总之,如果 update 语句更新的是普通字段的值,就会对发生更新的记录加 X 型记录锁。

但是,如果 update 语句更新的是索引的值,那么在运行的时候会被拆分成删除和插入操作,这时候分析锁的时候,要从这两个操作的角度去分析。

完啦!

怎么样,够不够细节?

本文地址:http://www.bzuk.cn/html/099a9599805.html
版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。

热门文章

全站热门

尼康200.500镜头评测(探究尼康200.500镜头的特点和优势)

电脑开机报警长鸣教程(解决电脑开机时长鸣报警的有效方法)

最近参与的项目都与Linux有关,遇到的问题也特别多,这不今天就有一个同事跟我说他遇到的问题:他在windows下开启了一个NFS服务器,然后在Ubuntu下挂载,总是不成功。查看了一下他的windows服务器设置,没有问题;在Ubuntu下也能挂载其他NFS服务器。经过排查,最近确定是防火墙造成挂载失败,其解决方法如下所示(以Windows7系统为例)。方法一、把特殊的端口加入入站规则1、打开Windows防火墙,打开后选择“高级设置”项。2、在高级设置页面可以看到有一个“入站规则”,如下图所示。3、右键点击该选项,在弹出的下拉菜单中选择“新建规则...”。4、选择“端口”项,再点“下一步”继续。5、接下来先选择“TCP”,“UDP”项等设置完后再来设置。下面一栏选择“特定本地端口”,输入111、1058、2049后选择“下一步”。6、然后选择“允许连接”,点“下一步”继续。7、这一步默认设置,点“下一步”继续后面的设置。8、最后是要求输入个名称,输入后点“完成”结束。9、UDP的设置跟TCP的设置一样,这里就不再多说了。方法二、关闭防火墙1、从开始菜单下找到“控制面板”,点击进入控制面板。2、在“控制面板”页面下,找到“系统和安全”项,点击进入。3、然后找到“Windows防火墙”,如下图所示,点击进入下一页。4、在左侧页面可以看到“打开或关闭Windows防火墙”,如下图所示,点击进入。5、接下来如下图所示,选择“关闭Windows防火墙(不推荐)”。6、以上配置好后,点“确定”即可关闭防火墙。7、防火墙关闭后的页面如下图所示。假如需要重新开启,选择左侧的“打开或关闭Windows防火墙”项设置一下即可。相关推荐: Ubuntu系统怎么手动进行更新升级 Ubuntu 15.10候最终选版ISO镜像下载 Ubuntu 14.04怎使用vsftpd搭建FTP服务

播放本地音乐或者收听国外的音乐电台,Ubuntu 14.04 自带的音乐播放器 Rhythmbox 完全能够满足,但是假如你想有像酷狗那样的国内播放器就需要折腾一下,还好有深度音乐播放器,这是一款完全为中国人开发的音乐播放器,深度音乐播放器(Dmusic)+ 百度音乐插件=酷狗,但是假如是deepin系统用户就完全不需要折腾了。先截图一下:安装方法(注释:我的系统是Ubuntu 14.04 其他系统没有实验,所以不保证是否成功)先安装深度音乐播放器,安装很方便,有PPA可用,不过安装之前需要安装依赖包gstreamer0.10-ffmpeg,然后安装Dmusic,打开终端输入以下命令:sudo add-apt-repository ppa:noobslab/deepin-scsudo apt-get updatesudo apt-get install deepin-music-player安装完成之后,假如在Dash里面搜索不到深度音乐,重启一下系统。第一次启动会有一个向导:安装百度音乐插件(深度音乐播放器):1、安装 cython libwebkitgtk-dev python-dev git, 打开终端,输入以下命令:sudo apt-get install cython libwebkitgtk-dev python-dev git2、安装pyjavascriptcore,打开终端,输入以下命令:git clone https://github.com/sumary/pyjavascriptcore.gitcd pyjavascriptcoresudo python setup.py install3、安装百度音乐插件,打开终端,输入以下命令:git clone https://github.com/sumary/dmusic-plugin-baidumusic.gitcd dmusic-plugin-baidumusiccp -r baidumusic ~/.local/share/deepin-music-player/plugins/运行深度音乐, 选项设置->附加组件 中启用百度音乐即可

解决iPad恢复过程中出现未知错误的方法(如何应对iPad恢复时遇到未知错误?解决办法一览)

使用电脑安装飞利浦手机的教程(一步步教你如何将飞利浦手机与电脑成功连接和安装)

gnome和KDE只是不同的桌面管理器而已,软件阿什么的是不受影响的,放心装吧。下面是安装KDE的过程:sudo apt-get install kubuntu-desktop遇到选yes or no的yes就行,之后会有一个蓝屏的文字窗口,寻问你要选择Kdm 还是gdm,这个看个人喜好,因为这只是开机登陆窗是KDE的还是GNOME的差别而已。我选择的是gdm,按回车,OK重启Xwindows(Ctrl+Alt+Backspace),在登陆窗的“会话”中选择KDE,登陆后就是变成KDE了(假如想回到ubuntu的话,可以在“会话”中改成gnome)注意:一定不用担心兼容问题,在ubuntu下安装的一切软件都可以在Kubuntu下使用,同理,Ubuntu下也可以使用KDE的软件。简言之,所以软件是共用的。KDE为英文界面,可以为它安装中文环境:sudo apt-get install language-pack-kde-zh language-pack-kde-zh-base language-pack-zh language-pack-zh-base language-support-zh重启Xwindows就行了。

升级好ubuntu15.04之后,却发现系统语言全是英文,该怎么设置中文呢,我这就把方法分享给大家。1、点击系统左侧菜单栏中的那个“扳手+齿轮”图标,该图标是打开系统设置的图标。2、打开上面的那个图标后。在第一行,我们使用鼠标左键点击,最后的那个像国旗的图标。它是语言支持图标哦,如下图,打开它。3、打开那个图标之后,点击窗口中,从上到下的第二个图标,我这里是这个界面是中文的,所以,大家知道是在干什么!点击那个位置的按钮就行啦。4、点开后,设置里面有很多语言类型,我们拉动滚动条,来找到我们需要的语言。5、勾选语言列表中的“中文简体”或叫“chinese(simplified)”,然后点击左下角的那个按钮,假如你的此界面和我一样,那么就是点击的应用变更按钮。假如是英文的,勾选然后点击那个位置的按钮即可。6、在弹出的验证窗口里面,我们需要输入密码才能做到系统语言的变更,那么我们输入电脑的密码,然后点击如图中位置的按钮即可。7、我们把在语言支持列表(第一个是English)中的汉语,拉到English上面去,意思是第一个系统语言。然后点击该界面下面的第一个按钮。如下图8、经过以上步骤,我们点击系统右上角的那个齿轮图标,然后再点击里面的“log out ”,即,倒数第三个选项。9、注销一次系统,注销之后,我们重新登陆,是不是系统成为中文的了呢?

热门文章

友情链接

滇ICP备2023006006号-33