MySQL InnoDB中的锁机制深入讲解
作者:YoungChen 发布时间:2024-01-13 14:41:40
写在前面
数据库本质上是一种共享资源,因此在最大程度提供并发访问性能的同时,仍需要确保每个用户能以一致的方式读取和修改数据。锁机制(Locking)就是解决这类问题的最好武器。
首先新建表 test,其中 id 为主键,name 为辅助索引,address 为唯一索引。
CREATE TABLE `test` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` int(11) NOT NULL,
`address` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idex_unique` (`address`),
KEY `idx_index` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4;
INSERT 方法中的行锁
可见,如果两个事务先后对主键相同的行记录执行 INSERT 操作,因为事务 A 先拿到了行锁,事务 B 只能等待直到事务 A 提交后行锁被释放。同理,如果针对唯一索引字段 address 进行插入操作,也需要获取行锁,图同主键插入过程类似,不再重复。
但是,如果两个事务都针对辅助索引字段 name 进行插入,不需要等待获取锁,因为辅助索引字段即使值相同,在数据库中也是操作不同的记录行,不会冲突。
Update 方法与 Insert 方法结果类似。
SELECT FOR UPDATE 下的表锁与行锁
事务 A SELECT FOR UPDATE 语句会拿到表 test 的 Table Lock,此时事务 B 去执行插入操作会阻塞,直到事务 A 提交释放表锁后,事务 B 才能获取对应的行锁执行插入操作。
但是如果事务 A 的 SELECT FOR UPDATE 语句紧跟 WHERE id = 1 的话,那么这条语句只会获取行锁,不会是表锁,此时不阻塞事务 B 对于其他主键的修改操作
辅助索引下的间隙锁
先看下 test 表下的数据情况:
mysql> select * from test;
+----+------+---------+
| id | name | address |
+----+------+---------+
| 3 | 1 | 3 |
| 6 | 1 | 2 |
| 7 | 2 | 4 |
| 8 | 10 | 5 |
+----+------+---------+
4 rows in set (0.00 sec)
间隙锁可以说是行锁的一种,不同的是它锁住的是一个范围内的记录,作用是避免幻读,即区间数据条目的突然增减。解决办法主要是:
防止间隙内有新数据 * 入,因此叫间隙锁
防止已存在的数据,在更新操作后成为间隙内的数据(例如更新 id = 7 的 name 字段为 1,那么 name = 1 的条数就从 2 变为 3)
InnoDB 自动使用间隙锁的条件为:
Repeatable Read 隔离级别,这是 MySQL 的默认工作级别
检索条件必须有索引(没有索引的话会走全表扫描,那样会锁定整张表所有的记录)
当 InnoDB 扫描索引记录的时候,会首先对选中的索引行记录加上行锁,再对索引记录两边的间隙(向左扫描扫到第一个比给定参数小的值, 向右扫描扫描到第一个比给定参数大的值, 以此构建一个区间)加上间隙锁。如果一个间隙被事务 A 加了锁,事务 B 是不能在这个间隙插入记录的。
我们这里所说的 “间隙锁” 其实不是 GAP LOCK,而是 RECORD LOCK + GAP LOCK,InnoDB 中称之为 NEXT_KEY LOCK
下面看个例子,我们建表时指定 name 列为辅助索引,目前这列的取值有 [1,2,10]。间隙范围有 (-∞, 1]、[1,1]、[1,2]、[2,10]、[10, +∞)
Round 1:
事务 A SELECT ... WHERE name = 1 FOR UPDATE;
对 (-∞, 2) 增加间隙锁
事务 B INSERT ... name = 1 阻塞
事务 B INSERT ... name = -100 阻塞
事务 B INSERT ... name = 2 成功
事务 B INSERT ... name = 3 成功
Round 2:
事务 A SELECT ... WHERE name = 2 FOR UPDATE;
对 [1, 10) 增加间隙锁
事务 B INSERT ... name = 1 阻塞
事务 B INSERT ... name = 9 阻塞
事务 B INSERT ... name = 10 成功
事务 B INSERT ... name = 0 成功
Round 3:
事务 A SELECT ... WHERE name <= 2 FOR UPDATE;
对 (-∞, +∞) 增加间隙锁
事务 B INSERT ... name = 3 阻塞
事务 B INSERT ... name = 300 阻塞
事务 B INSERT ... name = -300 阻塞
InnoDB 锁机制总结
参考资料
《MySQL 技术内幕 InnoDB 存储引擎》第二版 姜承尧著
About MySQL InnoDB's Lock
来源:https://juejin.im/post/5cd0eb17e51d453aa307c81c


猜你喜欢
- 1.匿名函数介绍匿名函数指一类无须定义标识符的函数或子程序。Python用lambda语法定义匿名函数,只需用表达式而无需申明。在pytho
- python生成随机数都有哪些办法呢使用 random 模块:random模块是python内置的模块,使用方法如random.randin
- 目录一、 环境准备:1.docker环境2.安装mariadb数据库二、ORM1.ORM简介2.django配置数据库第一种方式:第二种方式
- 用户体验(User Experience,简称UE)是用户在使用产品过程中建立起来的一种纯主观感受。在基于Web的产品设计中,UE是一个相对
- 一个可能你似曾相识的场景阅读内容包含大量英文的 PPT、Word、Excel 或者记事本时,由于英语不熟悉,为了流利地阅读,需要打开浏览器进
- 设置AccessCount字段可以根据需求在特定的时间范围内如果是相同IP访问就在AccessCount上累加。Create table C
- MySQL分页分析原理及提高效率PERCONA PERFORMANCE CONFERENCE 2009上,来自雅虎的几位工程师带来了一篇”E
- 首先我们知道这个效果应该是一个老话题了。今天整理文件的时候,发现自己以前的一些布局的解决方法躺在文件夹里很长时间了,翻翻老底吧。需要说明的是
- 在学习python的时候,会有一些梗非常不适应,在此列举列表删除和多重循环退出的例子:列表删除里面的坑比如我们有一个列表里面有很多相同的值,
- 有人问为什么要去扫描网站目录:懂的人自然懂这个Python脚本的特点:1.基本完善2.界面美观(只是画了个图案)3.可选参数增加了线程数4.
- 内存溢出问题是参加kaggle比赛或者做大数据量实验的第一个拦路虎。以前做的练手小项目导致新手产生一个惯性思维——读取训练集图片的时候把所有
- <div> <table width="320" cellpadding="0"
- 前言通过后端接口的返回值,动态添加路由,是作为权限控制的一种常见方式,本文将简单讲解如何在Vue3中动态添加路由。示例数据[ {
- 本文实例讲述了python判断windows系统是32位还是64位的方法。分享给大家供大家参考。具体分析如下:通常64的windows系统p
- 1,CSS,JS,IMG一个都不能少运行代码框<style type="text/css">&l
- Python是一种计算机程序设计语言。是一种面向对象的动态类型语言,最初被设计用于编写自动化脚本(shell),随着版本的不断更新和语言新功
- 本文实例讲述了gearman+mysql方式实现持久化操作。分享给大家供大家参考,具体如下:1、为什么要持久化?gearman的job se
- 前言针对一些特殊的需求,在项目里,需要将响应式数据变为普通原始类型数据,这种情况是有的在Vue里,能够将普通数据类型的数据变为响应式数据,同
- 本文用于利用Pytorch实现神经网络的分类!!!1.训练神经网络分类模型import torchfrom torch.autograd i
- 使用jQuery的.post提交,并期望得到多个数据,Python后台要使用json格式。不指定datatype为json,让jquery自