首页
学习
活动
专区
工具
TVP
发布

深入研究——MySQL事务隔离级别与锁的关系问题

大家晚上好,今天重点研究一下MySQL事务与锁的问题。我敢说这个问题足以难倒90%的开发者,即使今天,我也只是略懂些皮毛而已。

好了,研究开始,首先,介绍一些预备知识。

一般情况下,不必对数据库显式加锁,因为DBMS的内部加锁机制已经帮我们做的妥妥的了,但是为了研究方便,还是需要人为加锁的,因为可控嘛。数据库锁可以根据用途从不同角度进行分类,本文就将锁划分为读锁(也叫共享锁,记为S)与写锁(也叫排他锁,记为X)。读锁的作用是某个事务对某些数据加了S锁之后,其他事务也可以对这些数据加S锁,也可以读取这些数据,但不能加X锁(或者进行写操作)。写锁的作用是某个事务对某些数据加上了X锁之后,其他事务不能对这些数据加任何锁。

如果要显式加锁,可以通过以下语句添加:

1.加S锁的方式:

select * from tableName [where ...] lock in share mode;

2. 加X锁的方式:

select * from tableName [where ...] for update;

3. insert、delete、update操作也会加X锁

我们就以MySQL事务的默认隔离级别(repeatable read)做为研究的起点吧,开启三个客户端,其中两个作为研究客户端,还有一个作为监测状态客户端。(点击可放大图片观看)

在客户端1中:

在客户端2中:

在客户端2更新阻塞的过程中,赶紧在监测客户端3中执行如下:

看见了吗?客户端2想要更新那条记录,但却被阻塞住了,查看状态才发现,客户端2处于LOCK WAIT(锁等待)状态,为什么呢?原因就是因为在客户端1中的事务中的那条记录已经被加上X锁了,此时就算是在客户端2对这条记录加S锁也不行,请看:

因为客户端1对这条记录加的是X锁嘛!

但是,如果在客户端2就查询一下这条记录是不会阻塞的,请看:

这回为什么呢?原因很简单,因为这条select根本就没有试图加任何锁!注意:有两种情况会出现阻塞,那就是锁冲突或者“死锁”的时候,人家这条select根本就没想着加锁,当然不会与客户端1的X锁冲突。

还有一种“诡异”的现象,如果我在客户端2中对stuid为226的那条记录(也就是“郭靖”)进行修改,不会阻塞:

但是,如果我使用名字对“郭靖”进行修改,就会发生阻塞,请看:

哈哈,奇怪吧,可“郭靖”明明就是stuid为226的那条记录啊,这回为什么会阻塞呢?

我有一个大胆但自认为很合理的猜想:因为stuid是student表的主键,主键是不能重复的,所以一旦确定了stuid为226,就能确定这条记录是唯一的,又发现这条记录没有被锁住,所以不阻塞。但如果用stuname='郭靖'去更新,那么数据库底层必然会一行一行地寻找名字是否为'郭靖'然后再决定是否更新,但这样一行一行地找,必然会遍历到'黄蓉'那条记录(也就是在客户端1加X锁的那条记录),这样不就锁住了嘛。当然,这一点,只是我的猜想。

各位,加油吧!

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180205G1EU8900?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券