前言

读锁写锁意向锁,表锁行锁页面锁。

在学习Java并发编程的时候,肯定少不了学习。最常见的就是synchronized,锁的概念不是很好理解,有的地方说是锁住了一段代码,有的地方说是锁住了一个对象。弄得初学者都是丈二和尚——摸不着头脑。

抛开这些结论性的说法,说一下我对锁的理解(不管是Java中的锁还是数据库中的锁,还是分布式锁)。当我们需要限制某段程序在同一时刻,最多能被1个线程同时执行的时候就需要锁。这个幸运的线程怎么选出来呢?那就让他们去抢一个许可证吧,重点是需要保证这个许可证是唯一的,一次最多只能被一个线程抢到。许可证不要拘泥于是this,还有可能是数据库设置了唯一键的列、缓存中唯一的key或者一个文件目录。只有抢到了这个许可证的线程,才可以执行这段代码,执行完成或者异常退出后自动释放许可证

理解了这点,再说锁住的是一段代码、一个对象,甚至是一个线程都无所谓了,因为锁的作用就是在某一段时间内将一段代码、一个对象(许可证)、一个线程绑定在一起。

MySQL中的锁与存储引擎有关,MyISAM只支持表级锁,InnoDB既支持表级锁,又支持行级锁

InnoDB中的锁

InnoDB实现了行级锁,可以分为两种类型:共享(S)锁定和排他(X)锁定。

  • 共享(S)锁允许持有该锁的事务读取一行,所有又叫读锁
  • 排他(X)锁允许持有该锁的事务更新或删除行,所以又叫写锁

多个事务并发执行时,如果事务T1在某一行r上持有共享(S)锁,那么事务T2的对这行r的锁请求将按以下方式处理:

  • T2对S锁的请求可以立即获得批准。T1和T2都在r上保持了S锁
  • T2对X锁的请求不能获取批准。

如果事务T1在某一行r上拥有排他(X)锁,则事务T2不能获取锁(不论是S锁还是X锁

换言之,S锁和S锁是兼容的,S锁和X锁是冲突的,X锁和X锁是冲突的

S锁 X锁
S锁 冲突 冲突
X锁 冲突 冲突

但是共享锁排它锁并不是指具体的两种锁,而是指两类锁。
同样的乐观锁悲观锁也不是指具体的锁,而是指两类锁。乐观锁是乐观的认为每次都不会发生冲突,只会在更新的时候检查要更新的值有没有被别人修改过,因为没有加锁,所以乐观锁又叫无锁。在数据库中一般是用MVCC实现乐观锁,在Java中用CAS实现乐观锁。至于悲观锁就是悲观的认为每次都会发生冲突,所以每次修改都需要加锁。

表锁

表锁是MySQL中粒度最大的一种锁,简单粗暴的锁住整张表,实现简单所以支持的并发度低。InnoDB和MyIASM都支持表锁,表锁分为共享(S)锁排他(X)锁
表锁的特点是实现简单,并发度低。加锁快,开销小。不会出现死锁。

行锁

行锁是MySQL中粒度最细的一种锁,每次只锁住要操作的那一行。实现复杂,支持的并发度高。只有InnoDB支持行锁。行锁也分为共享(S)锁排他(X)锁。行锁是对索引的锁定。例如SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE,防止任何其他事务插入,更新或删除t.c1值为10的行。行锁始终锁定索引,对于没有创建索引的表,InnoDB创建一个隐藏的聚簇索引并将该索引用于行锁。
行锁的特点是实现复杂,并发度高。加锁慢,开销大。会出现死锁。

意向锁(Intention Locks)

InnoDB支持多种粒度锁定,允许行锁表锁并存。为了使在多个粒度级别上的锁定变得切实可行,InnoDB实现了意图锁意向锁是表级锁,表示事务稍后对表中的行需要上哪种类型的锁(共享锁排他锁)。有两种类型的意图锁:
* 意向共享锁(IS)表示事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的 IS 锁。
* 意向排他锁(IX)表示事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的 IX 锁。

SELECT ... LOCK IN SHARE MODE设置IS锁定,而SELECT ... FOR UPDATE设置IX锁定
表级锁之间的兼容性如下

X锁 IX锁 S锁 IS锁
X锁 冲突 冲突 冲突 冲突
IX锁 冲突 兼容 冲突 兼容
S锁 冲突 冲突 兼容 兼容
IS锁 冲突 兼容 兼容 兼容

有的同学一看到这么多种情况就头晕,死记硬背是不可能死记硬背的,这辈子都不会死记硬背。既然这样,那就干脆不记,花点时间深入了解一下为什么要设计成表中这样。

要想理解这个表,首先得理解为什么要有意向锁(Intention Locks),关于意向锁的作用,官方文档上给出了这么一句话:

Intention locks do not block anything except full table requests (for example, LOCK TABLES … WRITE). The main purpose of intention locks is to show that someone is locking a row, or going to lock a row in the table.

意向锁不会阻止任何其他请求,除了(锁定)全表请求(例如LOCK TABLES ... WRITE)外。意向锁定的主要目的是:声明已经有事务正在锁定表中的行,或者即将锁定表中的行。

这句话透露出了意向锁虽然是表锁,但是和行锁是完全兼容的(包括共享锁排它锁)。但是声明表中的行正在被锁定或者即将被锁定,有什么用呢?

假设事务A给表中某一行加了排它锁,而事务B想给这个表加一个全表的排他锁,这时候事务B就需要判断当前表中到底有没有排他锁,如果有的话,是不能成功加上去表的排它锁的。此时,如果没有意向锁,事务B只能遍历整个索引去判断,这样无疑是低效的。为了解决这个问题,MySQL引入了意向锁。当事务A给某一行加了排它锁或者共享锁后,会分别在表上加意向排它锁(IX)或者意向共享锁(IS),这时,事务B就可很轻松的判断当前表是否有行锁,这也就是前文所说的:为了使在多个粒度级别上的锁定变得切实可行,InnoDB实现了意图锁

理解了意图锁的作用,再来看看上面的表格。S锁和X锁之间的兼容性前文已经理清了,还剩下IX和IS之间以及S/X和IS/IX的兼容性。

由于某一行被加了S锁或者X锁后,表上都会加上对应的IS锁和IX锁。另一个事务想锁住另一条记录,也得加上对应的IS锁或者IX锁,所以IS和IS、IS和IX以及IX和IX必定是兼容的,不然整个表最多只能上一个行锁。

至于S锁、X锁和IS锁、IX锁的兼容性则需与分情况讨论了。当整个表上了X锁之后,再也不能上别的X锁或者S锁了,所以X锁和IS、IX都是冲突的。当整个表上了S锁之后,不能再上X锁了,但是还可以上S锁,所以S锁和IX锁是冲突的,但是和IS锁是兼容的。

这样理解了之后,再看上面的那个表格,似乎也变得有规律了。

间隙锁(Gap Locks)

间隙锁锁定的是索引记录之间的间隙,或者在第一个或最后一个索引记录之前的间隙。例如SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE,可以防止其他事务将t.c1为15的记录插入表中,因为这两个值之间的间隙是被锁定的。
间隙可能跨越单个索引值,多个索引值,甚至为空。
间隙锁的唯一目的是防止其他事务在间隙中插入数据。间隙锁可以共存。一个事务执行的间隙锁,不会阻止另一事务对相同的间隙进行间隙锁定。

Next-Key Locks

Next-Key Locks是索引记录上的行锁索引记录之前的间隙上的间隙锁的组合。如果一个session在索引中的记录R上具有共享排他锁,则另一session不能按照索引顺序在R之前的间隙中插入新的索引记录。
默认情况下,InnoDB设置的事务隔离级别是REPEATABLE READ。在这种情况下,InnoDB使用Next-Key Locks进行搜索和索引扫描,这可以防止幻读(虚读)。关于MySQL隔离级别相关的问题,请参考:面试官:MySQL事务是怎么实现的

插入意图锁(Insert Intention Locks)

插入意图锁是在行插入之前,通过INSERT操作设置的间隙锁的一种类型。如果多个事务想要在同一个间隙中插入不同的值(也就是插入的位置不同),则这多个事务均不会被阻塞。假设有索引记录,其值分别为4和7。单独的事务分别尝试插入值5和6,在获得插入行的排他锁之前,每个事务都使用插入意图锁来锁定4和7之间的间隙,但不会互相阻塞,因为插入的行是无冲突的。

自增锁(AUTO-INC Locks)

AUTO-INC锁是一种特殊的表级锁,由事务插入具有AUTO_INCREMENT列的表中获得。在最简单的情况下,如果一个事务正在向表中插入值,则任何其他事务都必须等待这个事务在该表中进行插入,以便第一个事务插入的行接收连续的主键值。

MyISAM中的锁

相比之下MyISAM中的锁就简单多了,因为MyISAM只支持表锁。并且共享锁排它锁也满足如下关系

S锁 X锁
S锁 冲突 冲突
X锁 冲突 冲突

死锁

前文提到InnoDB行锁可能是出现死锁,死锁是一个计算机领域的概念,而不是数据库特有的,所以死锁的概念是通用的。不太了解死锁的同学请参考:面试官:请手写一段必然死锁的代码。这里演示下MySQL行锁导致的死锁,这也是官网上给出的例子
首先准备数据

## 创建表
CREATE TABLE t (i INT) ENGINE = InnoDB;## 新增数据
INSERT INTO t (i) VALUES(1);

具体操作的时间线如下

事务A 事务B
T1 START TRANSACTION
T2 SELECT * FROM t WHERE i = 1 LOCK IN SHARE MODE
T3 START TRANSACTION
T4 DELETE FROM t WHERE i = 1
T5 DELETE FROM t WHERE i = 1

事务A在T5执行时,事务B会收到一条错误信息

ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

此处发生死锁,因为事务A需要X锁才能删除该行。但是,不能授予该锁定请求,因为事务B已经具有X锁定请求,并且正在等待事务A释放其S锁定。由于B事先要求X锁,因此A持有的S锁也不能升级为X锁。结果,InnoDB为其中一个客户端生成一个错误并释放其锁。此时时,可以授予对另一个客户端的锁定请求,并从表中删除该行。

也就是说MySQL可以自动检测死锁,并且放弃一个事务来成全另一个事务,这点与Java程序中的死锁不一样。

查询事务、锁相关的参考命令如下:

## 查看当前事务状态
select trx_id, trx_state, trx_started, trx_requested_lock_id, trx_wait_started, trx_query, trx_isolation_level from information_schema.innodb_trx;## 查看当前锁定的事务
select * from information_schema.innodb_locks;## 查看当前正在等待锁的事务
select * from information_schema.innodb_lock_waits;

总结

锁是MySQL非常重要的一个部分,虽然一般情况下锁的锁定和释放都由MySQL自动完成。但是了解MySQL中的锁还是很有必要,它让我们进一步的了解了MySQL是如何处理并发的。

参考

  • https://dev.mysql.com/doc/refman/5.6/en/innodb-locking.html
  • https://zhuanlan.zhihu.com/p/29150809
  • https://www.zhihu.com/question/51513268/answer/127777478
  • https://dev.mysql.com/doc/refman/5.6/en/innodb-deadlocks.html

强人锁男,MySQL到底有多少锁?相关推荐

  1. 1. 请简述mysql数据库的锁机制_【MySQL入门】之MySQL数据库的锁机制(二)

    上篇文章主要聊了全局锁和表锁,并详细分析MDL锁的作用以及可能带来的问题.今天我们主要来聊一聊Innodb存储引擎的行锁.MySQL的行锁是在引擎层由引擎自己实现的,并不是所有的引擎都支持行锁,MyI ...

  2. mysql行锁怎么读_MySQL锁(三)行锁:幻读是什么?如何解决幻读?

    概述 前面两篇文章介绍了MySQL的全局锁和表级锁,今天就介绍一下MySQL的行锁. MySQL的行锁是各个引擎内部实现的,不是所有的引擎支持行锁,例如MyISAM就不支持行锁. 不支持行锁就意味着在 ...

  3. MySQL mutex互斥锁

    在事务机制中,锁机制是为了保证高并发,数据一致性的重要实现方式.MySQL除了Innodb引擎层面的行锁,还有latch锁.latch锁的作用资源协调处理.包含表句柄,线程,cpu线程,内存池等.其保 ...

  4. 灵魂拷问,MySQL到底能否解决幻读问题

    先说结论,MySQL 存储引擎 InnoDB 在可重复读(RR)隔离级别下是解决了幻读问题的. 方法:是通过next-key lock在当前读事务开启时,1.给涉及到的行加写锁(行锁)防止写操作:2. ...

  5. MySQL到底是如何解决幻读问题

    要知道什么是幻读,首先要知道以下四点: 一.幻读定义 幻读是指在同一个事务中,存在前后两次查询同一个范围的数据,但是第二次查询却看到了第一次查询没看到的行,一般情况下特指事务执行中新增的其他行. 二. ...

  6. mysql什么场景下要防止幻读_灵魂拷问,MySQL到底能否解决幻读问题

    先说结论,MySQL 存储引擎 InnoDB 在可重复读(RR)隔离级别下是解决了幻读问题的. 方法:是通过next-key lock在当前读事务开启时,1.给涉及到的行加写锁(行锁)防止写操作:2. ...

  7. 什么是乐观锁,什么是悲观锁

    一.并发控制 当程序中可能出现并发的情况时,就需要通过一定的手段来保证在并发情况下数据的准确性,通过这种手段保证了当前用户和其他用户一起操作时,所得到的结果和他单独操作时的结果是一样的.这种手段就叫做 ...

  8. 【MySQL】记录锁?间隙锁?临键锁?到底锁了些什么?这一篇帮你捋清楚( ̄∇ ̄)/

    特别强调- 本测试使用的是MySQL 8.0.27- 8.0.27- 8.0.27(因为不同版本命令可能会有差异哈) 打开两个终端,分别连接上MySQL,使用select @@global.trans ...

  9. mysql默认乐观锁悲观锁_MySQL中悲观锁和乐观锁到底是什么?-阿里云开发者社区...

    索引和锁是数据库中的两个核心知识点,隔离级别的实现都是通过锁来完成的 按照锁颗粒对锁进行划分 ? 锁用来对数据进行锁定,我们可以从锁定对象的粒度大小来对锁进行划分,分别为行锁.页锁和表锁. 行锁就是按 ...

最新文章

  1. angular初步认识一
  2. 使用OpenCV实现车道线检测
  3. YYCache 源码学习(一):YYMemoryCache
  4. Ctrl + z 和 Linux jobs
  5. MySQL 中的运算符
  6. 汉字转拼音插件:LM-PinYin.js
  7. python个人赚钱攻略-每个懂Python的人都必须知道的赚钱神器
  8. Python开发【第十二篇】:DOM
  9. 音视频技术开发周刊 53期
  10. 小程序开发中遇到的问题
  11. lsqcurvefit拟合结果为复数_使用科学计算器计算复数与相量(提高篇)
  12. Python:PyCharm提示Local variable ‘x‘ value is not used
  13. 【UE4】如何获取/下载虚幻4(Unreal Engine4)源码
  14. SQL查询语句之查询数据
  15. python:实现哥德巴赫猜想
  16. 用Python和OpenCV实现照片马赛克拼图(蒙太奇照片)
  17. 在IE地址栏显示自己的小图标
  18. 抓取整个网站图片的爬虫
  19. linux 软链接 相对路径,Linux入门之ln命令创建软链接的绝对路径和相对路径详解(Ubuntu)...
  20. 安卓输入法 车机版_百度输入法车载版-百度输入法车机版v8.2.1.18 安卓版-腾牛安卓网...

热门文章

  1. 在IDEA中如何取消打过的断点
  2. 以太联盟 基于区块链技术的角色扮演对战游戏
  3. 人民币金额由阿拉伯数值转换成汉字大写数值的函数
  4. 跟着团子学SAP DMS—在SAP中通过DMS上传文档基本操作(CV01N/CV02N/CV03N/CV04N)
  5. 求最长递增子序列个数——C++
  6. wsl 2 中安装docker
  7. 一张表左关联另外两张表,三表关联
  8. (二)基于kubernetes(1.25.2) 进行基础prometheus监控
  9. OSM数据分析及图形化显示以及各组员之间的关系Relation的存储
  10. Android 将drawable下的图片转换成bitmap、Drawable