深入了解mysql--gap locks,Next-Key Locks
Next-Key Locks
Next-Key Locks是在存储引擎innodb、事务级别在可重复读的情况下使用的数据库锁,官网上有介绍,Next-Key Locks是行锁和gap锁的组合。行锁是什么我们都很清楚,这篇文章主要简单分析一下mysql中的gap锁是什么。innodb默认的锁就是Next-Key locks。
GAP锁
gap锁,又称为间隙锁。存在的主要目的就是为了防止在可重复读的事务级别下,出现幻读问题。
在可重复读的事务级别下面,普通的select读的是快照,不存在幻读情况,但是如果加上for update的话,读取是已提交事务数据,gap锁保证for update情况下,不出现幻读。
那么gap锁到底是如何加锁的呢?
假如是for update级别操作,先看看几条总结的何时加锁的规则。
- 唯一索引
- 精确等值检索,Next-Key Locks就退化为记录锁,不会加gap锁
- 范围检索,会锁住where条件中相应的范围,范围中的记录以及间隙,换言之就是加上记录锁和gap 锁(至于区间是多大稍后讨论)。
- 不走索引检索,全表间隙加gap锁、全表记录加记录锁
- 非唯一索引
- 精确等值检索,Next-Key Locks会对间隙加gap锁(至于区间是多大稍后讨论),以及对应检索到的记录加记录锁。
- 范围检索,会锁住where条件中相应的范围,范围中的记录以及间隙,换言之就是加上记录锁和gap 锁(至于区间是多大稍后讨论)。
- 非索引检索,全表间隙gap lock,全表记录record lock
gap锁演示例子
假如有以下一张表结构,主键简单点是字符,属性列只有一个数字,是非唯一索引。
create table gap_table
(
letter varchar(2) default '' not null primary key,
num int not null
);
create index gap_table_num_uindex on gap_table (num);
INSERT INTO gap_table (letter, num) VALUES ('d', 3);
INSERT INTO gap_table (letter, num) VALUES ('g', 6);
INSERT INTO gap_table (letter, num) VALUES ('j', 8);
无gap锁
假如没有gap锁,也就是把事务级别调到读提交,执行以下两个session
| session1 | session2 |
|---|---|
| select * from gap_table where num=6 for update,结果是一条 | |
| INSERT INTO gap_table (letter, num) VALUES (’’, 6); | |
| select * from gap_table where num=6 for update,结果是二条,出现幻读 |
非唯一索引等值检索gap锁
假如有gap锁,演示一个非唯一索引等值检索gap锁。也就是把事务级别调到可重复读,执行以下两个session
| session1 | session2 |
|---|---|
| select * from gap_table where num=6 for update,结果是一条。 | |
| INSERT INTO gap_table (letter, num) VALUES (’’, 6);gap锁住间隙,阻塞无法插入数据。 | |
| select * from gap_table where num=6 for update,结果是一条。不出现幻读 |
唯一索引(主键)范围检索gap锁
假如有gap锁,演示一个唯一索引范围检索gap锁。也就是把事务级别调到可重复读,执行以下两个session
| session1 | session2 |
|---|---|
| select * from gap_table where letter>‘d’ for update,结果是两条。 | |
| INSERT INTO gap_table (letter, num) VALUES (‘z’, 10);gap锁住间隙,阻塞无法插入数据。 | |
| select * from gap_table where letter>‘d’ for update,结果是两条。不出现幻读 |
gap锁是如何锁区间?
经过上面的演示可以知道gap锁的基本作用就是保证可重复读的情况下不出现幻读。那么还有一点就是gap是按照什么原则进行锁的呢?要了解gap锁的原则,需要先了解innodb中索引树的结构。下面一张图片描述了在innodb中,索引的数据结构是如何组织的

从上面的图片可以看出,索引结构分为主索引树和辅助索引树,辅助索引树的叶子节点中包含了主键数据,主键数据影响着叶子节点的排序,gap锁的关键就是锁住索引树的叶子节点之间的间隙,不让新的记录插入到间隙之中,说起来可能拗口,下面画图分析。
非唯一索引gap锁原则分析
假如还是使用一开始演示的表结构和数据,那么当前的辅助索引树(数字列)叶子节点的排序结构应该如下。

假如执行以下sql的话
INSERT INTO gap_table (letter, num) VALUES ('k', 6);
辅助索引树的叶子节点结构变为以下图片结构,k大于g,所以(6,k)排在后面,我们先把(6,k)这条数据删除,方便后面演示。

了解了以上的规则,我们进行实际操作演示gap锁区间原则,从而推测锁住哪些区间。
情况1
分别有两个session,session1执行以下语句:
select * from gap_table where num=6 for update
session2执行以下sql,执行成功:
INSERT INTO gap_table (letter, num) VALUES ('a', 3);
按照排序规则,叶子节点插入结构如下

情况2
分别有两个session,session1执行以下语句:
select * from gap_table where num=6 for update
session2执行以下sql,执行失败:
INSERT INTO gap_table (letter, num) VALUES ('e', 3);
按照排序规则,叶子节点应该插入如下地方,但是因为区间被锁插入失败。

情况3
分别有两个session,session1执行以下语句:
select * from gap_table where num=6 for update
session2执行以下sql,执行失败:
INSERT INTO gap_table (letter, num) VALUES ('h', 6);
按照排序规则,叶子节点应该插入如下地方,但是因为区间被锁插入失败。

情况4
分别有两个session,session1执行以下语句:
select * from gap_table where num=6 for update
session2执行以下sql,执行失败:
INSERT INTO gap_table (letter, num) VALUES ('h', 7);
按照排序规则,叶子节点应该插入如下地方,但是因为区间被锁插入失败。

情况5
分别有两个session,session1执行以下语句:
select * from gap_table where num=6 for update
session2执行以下sql,执行成功:
INSERT INTO gap_table (letter, num) VALUES ('h', 9);
按照排序规则,插入在未锁区间就能插入成功。

总结
当session1执行以下语句:
select * from gap_table where num=6 for update
锁住的区间如图所示。按照B+索引树排序规则,计算好叶子节点插入位置时,在被gap锁住的区间段内,不能插入任何数据,只有在gap锁释放时才能进行插入。

在上面的各种情况中锁住的区间其实是(3,d)到(6,g)和(6,g)到(8,j),落到这个区间段的叶子节点都是无法插入的。主键也作为一个信息参与到叶子节点的排序规则中。这里面边界都是开区间,插入(3,d),(8,j)的数据会报错主键重复而不是lock等待超时。
唯一索引或者非唯一索引范围检索gap锁原则分析
另一种会出现gap锁的情况就是使用索引时,用到范围检索,就会出现gap 锁。
使用以下表结构。
create table gap_tbz
(
id int default 0 not null
primary key,
name varchar(11) not null
);
INSERT INTO test.gap_tbz (id, name) VALUES (1, 'a');
INSERT INTO test.gap_tbz (id, name) VALUES (5, 'h');
INSERT INTO test.gap_tbz (id, name) VALUES (8, 'm');
INSERT INTO test.gap_tbz (id, name) VALUES (11, 'ds');
情况1
分别有两个session,session1执行以下语句:
select * from gap_tbz where id > 5 for update;
session2执行以下sql,执行失败:
insert into gap_tbz values(6,'cc');
按照排序规则,这里应该是在主键索引树检索,叶子节点插入结构如下。由于session1执行了范围的for update sql语句,因此范围内添加了gap锁,gap锁的区间是id在(5,+无限)

当执行插入的id范围在5之前,如下sql,能够执行成功。
insert into gap_tbz values(4,'cc');
情况2
分别有两个session,session1执行以下语句:
select * from gap_tbz where id > 5 and id < 11 for update;
session2执行以下sql,执行失败:
#以下报错 lock等待超时
insert into gap_tbz values(11,'cc');
#以下报错 主键重复
insert into gap_tbz values(5,'cc');
#从两种报错来看也可以看出gap锁区间是左开右闭
按照排序规则,这里应该是在主键索引树检索,由于session1执行了范围的for update sql语句,因此范围内添加了gap锁,gap锁的区间是id在(5,11],唯一索引gap锁区间是左开右闭。
思考
假如条件是一个非索引列,那么如何处理?
假如是非索引咧,那么将会全表间隙加上gap锁。
条件是唯一索引等值检索且记录不存在的情况,会使用gap lock?
我们要考虑,gap lock是防止幻读,那么尝试思考,使用唯一索引所谓条件查找数据for update,如果对应的记录不存在的话,是无法使用行锁的。这时候,会使用gap lock来锁住区间,保证记录不会插入,防止出现幻读。
文章转载自:https://blog.csdn.net/tb3039450/article/details/66475638
深入了解mysql--gap locks,Next-Key Locks的更多相关文章
- MySQL Gap Lock问题
四种隔离级别说明 隔离级别 脏读(Dirty Read) 不可重复读(NonRepeatable Read) 幻读(Phantom Read) 未提交读(Read uncommitted) 可能 可能 ...
- MySQL中ON DUPLICATE KEY UPDATE使用
今天做推断插入用到了MySQL中ON DUPLICATE KEY UPDATE,如今Mark下面! 假设你想做到数据库中没有数据的话插入数据.有数据的话更新数据,那么你能够选择ON DUPLICATE ...
- mySQL中删除unique key的语法 (删除某个字段的唯一性)
mySQL中删除unique key的语法 CREATE TABLE `good_booked` ( `auto_id` int(10) NOT NULL auto_increment, `goo ...
- Using INSERT IGNORE with MySQL to prevent duplicate key errors
An error will occur when inserting a new record in MySQL if the primary key specified in the insert ...
- MySQL于ON DUPLICATE KEY UPDATE采用
今天我们做的推断插入用途MySQL于ON DUPLICATE KEY UPDATE.现在,Mark下面! 假设你想做的事,再有就是在数据库中插入数据没有数据.如果有数据更新数据,然后你可以选择ON D ...
- mysql优化 ON DUPLICATE KEY UPDATE
场景:比如,有一张表,专门记录业务里的唯一数据记录,这张表里如果存在此唯一数据的记录就更新此行数据的某个字段,如果此唯一数据不存在,那么就添加一条最新数据. 一贯操作:如果不知道mysql有 ON D ...
- [MySQL] gap lock/next-key lock浅析
当InnoDB在判断行锁是否冲突的时候, 除了最基本的IS/IX/S/X锁的冲突判断意外, InnoDB还将锁细分为如下几种子类型: record lock (RK) 记录锁, 仅仅锁住索引记录的一行 ...
- MYSQL外键(Foreign Key)的使用
在MySQL 3.23.44版本后,InnoDB引擎类型的表支持了外键约束.外键的使用条件:1.两个表必须是InnoDB表,MyISAM表暂时不支持外键(据说以后的版本有可能支持,但至少目前不支持): ...
- Mysql如何修改unique key
link:http://www.netingcn.com/mysql-modifyunique-key.html mysql可以使用unique key来确保数据的准确性,unique key可以是一 ...
- 在 mysql 中利用 Duplicate key, 一句话实现存在的更新不存在插入功能
mysql 中可以用一个sql命令实现在插入时,如果发现唯一索引重复的记录则自动改为更新语句, 语句如下: '; 注意,radcheck 表中 username 和 attribute 列是个组合的唯 ...
随机推荐
- shell脚本 awk实现实时监控网卡流量
一.简介 通过第3方工具获得网卡流量,这个大家一定很清楚.其实通过脚本一样可以实现效果.下面是我个人工作中整理的数据.以下是shell脚本统计网卡流量. 现原理: cat /proc/net/dev ...
- 数据类型Table.TransformColumnTypes(Power Query 之 M 语言)
数据源: 任意数据源 目标: 设置适合的数据类型 操作过程: 选取指定列>[主页]>[数据类型]>选取 选取指定列>[转换]>[数据类型]>选取 选取指定列> ...
- 小迪安全 Web安全 基础入门 第七天 - 资产泄漏、CMS识别、Git监控、SVN、DS_Store、备份
一.CMS指纹识别源码获取方式 1.网站特有文件.如/templets/default/style/dedecms.css-dedecms. 2.网站独有文件的MD5.如favicon.ico但是该文 ...
- LuoguB2105 矩阵乘法 题解
Content 给定一个 \(n\times m\) 的矩阵 \(A\) 和一个 \(m\times k\) 的矩阵 \(B\),求两个矩阵相乘得到的矩阵. \(n\times m\) 的矩阵 \(A ...
- python3 5月26日 time模块常用时间转换 &datetime()模块学习 random()
import time 获取当前时间: 指定字符串格式:time.strftime("%Y-%m-%d %H:%M:%S") 当前时间戳:time.time() 当前时间元组格式 ...
- 直接在filter过滤器代码里加org.apache.struts2.ServletActionContext.getRequest()会出现空指针情况
直接在filter过滤器代码里加org.apache.struts2.ServletActionContext.getRequest()获得request对象请注意啦,会出现空指针情况 请关注此处:
- libevent源码学习(17):缓冲管理框架
目录Libevent缓冲区类型Libevent缓冲区结构缓冲区的读出与写入缓冲区的读入与写出缓冲区水位机制缓冲区回调机制延迟回调机制Libevent缓冲区类型 Libevent中提供了多种 ...
- Vue-Router(一)
Vue-Router(一) 简介 vue-router是Vuejs的官方推荐路由,让用 Vue.js 构建单页应用变得非常容易.目前Vue路由最新的版本是4.x版本. vue-router是基于路由和 ...
- js(JQuery)引入select2
官方项目地址:https://select2.org/ 引入css和js <link href="https://cdnjs.cloudflare.com/ajax/libs/sele ...
- htmlunit设置只采集html,取消对css,javascript支持
引入htmlunit依赖 <!-- https://mvnrepository.com/artifact/net.sourceforge.htmlunit/htmlunit --> < ...