各位对 ”锁“ 这个概念应该都不是很陌生吧,Java 语言中就提供了两种锁:内置的 synchronized 锁和 Lock 接口,使用锁的目的就是管理对共享资源的并发访问,保证数据的完整性和一致性,数据库中的锁也不例外。

“锁" 是数据库系统区别于文件系统的一个关键特性,其对象是事务,用来锁定的是数据库中的对象,如表、页、行等。需要注意的是,每种数据库对于锁的实现都是不同的,并且对于 MySQL 来说,每种存储引擎都可以实现自己的锁策略和锁粒度,比如 InnoDB 引擎支持行锁和表锁,而 MyISAM 引擎只支持表锁

本文所讲的锁针对的是我们最常用的 InnoDB 存储引擎。

表锁与行锁

所谓 “表锁 (Table Lock)”,就是会锁定整张表,它是 MySQL 中最基本的锁策略,并不依赖于存储引擎,就是说不管你是 MySQL 的什么存储引擎,对于表锁的策略都是一样的,并且表锁是开销最小的策略(因为粒度比较大)。

由于表级锁一次会将整个表锁定,所以可以很好的避免死锁问题。当然,锁的粒度大所带来最大的负面影响就是出现锁资源争用的概率也会最高,导致并发率大打折扣。

而所谓 “行锁(Row Lock)”,也称为记录锁,顾名思义,就是锁住某一行(某条记录 row)。需要的注意的是,MySQL 服务器层并没有实现行锁机制,行级锁只在存储引擎层实现 !!!

读锁和写锁

首先说明一点,对于 InnoDB 引擎来说,读锁和写锁可以加在表上,也可以加在行上

对于并发读和并发写的问题,可以通过实现一个由两种类型的锁组成的锁系统来解决。这两种类型的锁通常被称为 共享锁(Shared Lock,S Lock)排他锁(Exclusive Lock,X Lock),也叫 读锁(readlock)写锁(write lock)

  • 共享锁 / 读锁:允许事务读(select)数据
  • 排他锁 / 写锁:允许事务删除(delete)或更新(update)数据

读锁是共享的,或者说是相互不阻塞的。多个事务在同一时刻可以同时读取同一个资源,而互不干扰。写锁是排他的,也就是说一个写锁会阻塞其他的读锁和写锁,这样就能确保在给定的时间里,只有一个事务能执行写入,并防止其他用户读取正在写入的同一资源。

用行级读写锁来举个例子吧:如果一个事务 T1 已经获得了某个行 r 的读锁,那么此时另外的一个事务 T2 是可以去获得这个行 r 的读锁的,因为读取操作并没有改变行 r 的数据;但是,如果某个事务 T3 想获得行 r 的写锁,则它其必须等待事务 T1、T2 释放掉行 r 上的读锁才行。

兼容关系如下表(兼容是指对同一张表或记录的锁的兼容性情况):

X 锁 S 锁
X 锁 不兼容 不兼容
S 锁 不兼容 兼容

从上表可以看出,只有共享锁和共享锁是兼容的,而排他锁和谁都是不兼容的。

意向锁

InnoDB 存储引擎支持 多粒度(granular)锁定,就是说允许事务在行级上的锁和表级上的锁同时存在

那么为了实现行锁和表锁并存,InnoDB 存储引擎就设计出了 意向锁(Intention Lock) 这个东西:

Intention locks are table-level locks that indicate which type of lock (shared or exclusive) a transaction requires later for a row in a table.

很好理解:意向锁是一个表级锁,其作用就是指明接下来的事务将会用到哪种锁。

有两种意向锁:

  • 意向共享锁(IS Lock):当事务想要获得一张表中某几行的共享锁行级锁)时,InnoDB 存储引擎会自动地先获取该表的意向共享锁(表级锁)
  • 意向排他锁(IX Lock):当事务想要获得一张表中某几行的排他锁(行级锁)时,InnoDB 存储引擎会自动地先获取该表的意向排他锁(表级锁)

各位其实可以直接把 ”意向“ 翻译成 ”想要“,想要共享锁、想要排他锁,你就会发现原来就这东西啊(滑稽)。

意向锁之间是相互兼容的:

IS 锁 IX 锁
IS 锁 兼容 兼容
IX 锁 兼容 兼容

但是与表级读写锁之间大部分都是不兼容的:

X 锁 S 锁
IS 锁 不兼容 兼容
IX 锁 不兼容 不兼容

注意,这里强调一点:上表中的读写锁指的是表级锁,意向锁不会与行级的读写锁互斥!!!

来理解一下为什么说意向锁不会与行级的读写锁互斥。举个例子,事务 T1、事务 T2、事务 T3 分别想对某张表中的记录行 r1、r2、r3 进行修改,很普通的并发场景对吧,这三个事务之间并不会发生干扰,所以是可以正常执行的。

这三个事务都会先对这张表加意向写锁,因为意向锁之间是兼容的嘛,所以这一步没有任何问题。那如果意向锁和行级读写锁互斥的话,岂不是这三个事务都没法再执行下去了,对吧。

OK,看到这里,我们来思考两个问题:

1)为什么没有意向锁的话,表锁和行锁不能共存?

2)意向锁是如何让表锁和行锁共存的?

首先来看第一个问题,假设行锁和表锁能共存,举个例子:事务 T1 锁住表中的某一行(行级写锁),事务 T2 锁住整个表(表级写锁)。

问题很明显,既然事务 T1 锁住了某一行,那么其他事务就不可能修改这一行。这与 ”事务 T2 锁住整个表就能修改表中的任意一行“ 形成了冲突。所以,没有意向锁的时候,行锁与表锁是无法共存的。

再来看第二个问题,有了意向锁之后,事务 T1 在申请行级写锁之前,MySQL 会先自动给事务 T1 申请这张表的意向排他锁,当表上有意向排他锁时其他事务申请表级写锁会被阻塞,也即事务 T2 申请这张表的写锁就会失败。

如何加锁

在说加锁之前,我们有必要了解下解锁机制。对于 InnoDB 来说,随时都可以加锁,但是并非随时都可以解锁。具体来说,InnoDB 采用的是两阶段锁定协议(two-phase locking protocol):即在事务执行过程中,随时都可以执行加锁操作,但是只有在事务执行 COMMIT 或者 ROLLBACK 的时候才会释放锁,并且所有的锁是在同一时刻被释放。

说完了解锁机制,再来讲讲加锁机制。

先来看如何加意向锁,它比较特殊,是由 InnoDB 存储引擎自己维护的,用户无法手动操作意向锁,在为数据行加读写锁之前,InooDB 会先获取该数据行所在在数据表的对应意向锁。

  • 再来看如何加表级锁:

1)隐式锁定:对于常见的 DDL 语句(如 ALTERCREATE 等),InnoDB 会自动给相应的表加表级锁

2)显示锁定:在执行 SQL 语句时,也可以明确显示指定对某个表进行加锁(lock table user read(write)

lock table user read; # 加表级读锁
unlock tables; # 释放表级锁
  • 如何加行级锁:

1)对于常见的 DML 语句(如 UPDATEDELETEINSERT ),InnoDB 会自动给相应的记录行加写锁

2)默认情况下对于普通 SELECT 语句,InnoDB 不会加任何锁,但是在 Serializable 隔离级别下会加行级读锁

上面两种是隐式锁定,InnoDB 也支持通过特定的语句进行显式锁定,不过这些语句并不属于 SQL 规范:

3)SELECT * FROM table_name WHERE ... FOR UPDATE,加行级写锁

4)SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE,加行级读锁

另外,需要注意的是,InnoDB 存储引擎的行级锁是基于索引的,也就是说当索引失效或者说根本没有用索引的时候,行锁就会升级成表锁

举个例子(这里就以比较典型的索引失效情况 “使用 or" 来举例),有数据库如下,id 是主键索引:

CREATE TABLE `test` (`id` int(11) NOT NULL AUTO_INCREMENT,`username` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

新建两个事务,先执行事务 T1 的前两行,也就是不要执行 rollback 也不要 commit:

这个时候事务 T1 没有释放锁,并且由于索引失效事务 T1 其实是锁住了整张表,此时再来执行事务 2,你会发现事务 T2 会卡住,最后超时关闭事务:

InnoDB 存储引擎中的表锁和行锁详解相关推荐

  1. InnoDB存储引擎中的各种锁

    什么是锁 现实生活中的锁是为了保护你的私有物品,在数据库中锁是为了解决资源争抢的问题,锁是数据库系统区别于文件系统的一个关键特性.锁机制用于管理对共享资源的并发访. 数据库系统使用锁是为了支持对共享资 ...

  2. MySQL的InnoDB存储引擎中,缓冲池中的Changer Buffer与系统表空间中的Changer Buffer的关系

    MySQL的InnoDB存储引擎中,缓冲池中和系统表空间中都存在Changer Buffer,那它们之间的关系是怎样的呢?先来一张InnoDB存储引擎的架构图: 翻阅了MySQL官网发现如下: 1.h ...

  3. 在MySQL的InnoDB存储引擎中count(*)函数的优化

    转载自  在MySQL的InnoDB存储引擎中count(*)函数的优化 写这篇文章之前已经看过了很多数据库方面的优化内容,大部分都是加索引.使用事务.要什么select什么等等.然而,只是停留在阅读 ...

  4. MySQL数据库锁构建_MySQL数据库InnoDB存储引擎中的锁机制

    00 – 基本概念 当并发事务同时访问一个资源的时候,有可能导致数据不一致.因此需要一种致机制来将访问顺序化. 锁就是其中的一种机制.我们用商场的试衣间来做一个比喻.试衣间供许多消费者使用.因此可能有 ...

  5. MySQL数据库InnoDB存储引擎中的锁机制--转载

    原文地址:http://www.uml.org.cn/sjjm/201205302.asp 00 – 基本概念 当并发事务同时访问一个资源的时候,有可能导致数据不一致.因此需要一种致机制来将访问顺序化 ...

  6. Oracle中多表联合更新处理方法详解

    ORACLE中多表关联更新与SQL Server和MYSQL的语法稍有不同,现将ORACLE中多表关联更新的几种方法处理如下: 一.创建测试表和添加测试数据 CREATE TABLE t_test01 ...

  7. MySQL InnoDB存储引擎

    呵呵哒... MySQL体系结构和存储引擎 首先要搞懂的是什么是数据库,什么是数据库实例. 数据库:物理操作系统文件或其他形式文件类型的集合. 实例:MySQL数据库由后台线程以及一个共享内存区组成, ...

  8. 不同存储结构的文件磁盘io操作次数_MySQL InnoDB存储引擎

    第1章 MySQL体系结构和存储引擎 1.1数据库和实例 数据库:物理操作系统文件或其他形式文件类型的集合.实例:MySQL数据库由后台线程以及一个共享内存区组成.共享内存可以被运行 的后台线程所共享 ...

  9. MySQL技术内幕-InnoDB存储引擎第2版-学习笔记-01

    MySQL技术内幕-InnoDB存储引擎第2版-学习笔记-01 1. MySQL体系结构和存储引擎 1.1 定义数据库和实例 数据库database: 物理操作系统文件或其他形式文件类型的集合. 当使 ...

最新文章

  1. 我对bgwriter.c 与 guc 关系的初步理解
  2. rocketmq 消息指定_RocketMq 实际案例–普通消息的发送
  3. 总结Java常见面试题和答案
  4. Angular CDK Layoout 检测断点
  5. 【Python】科赫雪花绘制
  6. java解决XSS攻击常用方法总结
  7. 洛阳php编程培训,洛阳PHP开发培训分享提高PHP开发效率的四个编程习惯
  8. java9 变化_Java 9 ← 2017,2019 → Java 13,来看看Java两年来的变化
  9. HTML基础用 表格做报表
  10. SpringMVC:生成Excel和PDF
  11. 第三次作业_03功能测试
  12. Java项目:springboot超市进销存管理系统
  13. 外卖 webpp2---hesdar组件
  14. html小于等于怎么输入,小于等于号怎么打?电脑和手机输入≤的方法!
  15. 关于ENVI中的ROI
  16. 马斯克用数字孪生开启航天工业大时代,工互2.0来了吗?
  17. RPA设计实施、开发和单元测试
  18. python 读取txt函数总结
  19. 设置windows10相应文件格式的打开方式
  20. (186)Verilog HDL:3位线性反馈移位寄存器

热门文章

  1. R语言大会:宏基因组数据分析和可视化套路总结
  2. GPB:菊粉改善糖脂代谢紊乱作用的机制(作者解读)
  3. numpy使用[]语法索引二维numpy数组中指定指定行之前所有数据行的数值内容(accessing rows in numpy array before specifc row)
  4. R语言ggplot2可视化:可视化多个图像并组合起来(如果没有对齐,看着丑陋))、保证组合的图像图像区域对齐或者图像整体对齐(Align plot areas in ggplot)
  5. R语言pmax函数和pmin函数按位计算向量最大值、最小值实战
  6. R语言KMeans聚类分析确定最优聚类簇数实战:期望最大化expectation-maximization准则(确定最优聚类簇数)
  7. R语言使用read.delim函数读取带分隔符的文本文件
  8. Python时间转换函数:时间转化为时间戳、时间戳转化为时间、当前日期、当前时间、星期几、前面或者后面多少天、年、月、日等
  9. flux读取不到数据_【Java】spring-webflux两种开发模式,获取不到使用ajax的post提交的数据?...
  10. java用for循环查询数据_使用for循环结果创建数据框