关于间隙锁

juebanxiaobai 2016-07-18 06:33:06
所使用到的表:

该表中插入1,2,3,....,101
执行如下操作:


问题:
1.为什么session_1执行mysql> select * from emp where empid = 102 for update;后,出现的间隙锁只是102本身还是还有其他?
2.为什么session_2执行mysql>insert into emp(empid,...) values(201,...); 会出现加锁?

...全文
825 21 打赏 收藏 转发到动态 举报
写回复
用AI写文章
21 条回复
切换为时间正序
请发表友善的回复…
发表回复
致命的西瓜 2016-07-20
  • 打赏
  • 举报
回复
引用 13 楼 lbxx159753 的回复:
[quote=引用 12 楼 wangjian0228 的回复:] [quote=引用 11 楼 lbxx159753 的回复:] [quote=引用 10 楼 wangjian0228 的回复:] [quote=引用 8 楼 lbxx159753 的回复:] 看for update的字面意思感觉就挺能明白为啥insert的时候加锁了啊,而且应该是表锁,因为mysql不确定你select完了后是否update这条记录的主键,如果你刚好update emp set empid=201;那么自然跟insert的那个冲突了。除非你能事先告诉mysql你select完后只更新102,就只会有行锁了。
next_key 锁会划分锁范围,例子中101--正无穷都加了同一个域的锁,如果更新在1到101中就是行锁了[/quote] 确实是next_key锁导致的。不过感觉有点晕了,行锁和record lock、next_key lock、gap lock等是什么关系,总称吗[/quote] 行锁是一类名称,record lock的实现是行锁 如对已存在id for update,gap lock 是域锁,更新范围数据时会加gap,next_key lock 是gap和record的组合,昨天刚看了官方文档,明确了很多概念[/quote] 多谢指教。我还有个疑问,就是mysql只提供表锁、s和x锁,而数据库引擎(比如Innodb)提供意向锁is和ix这种是吧,那么gap、next_key这种的实际上是在这四种所基础上实现出来的,是这样的吗。感觉各种各样的锁,概念好繁杂,他们之间的关系也很不清晰。同时哪些情况下会加那种锁,这个也很不明白。[/quote] 事务处理中读写锁是基础,不同的数据库引擎封装了读写锁后形成了gap,record...,is 和ix 也是基于s 和 x 为了增加读写锁的判断效率 ,个人学习锁的时候只是理解原理,没有特意去记住XXX锁的名字,而是在具体的业务场景中结合,自然的串起来了
lbxx159753 2016-07-20
  • 打赏
  • 举报
回复
引用 12 楼 wangjian0228 的回复:
[quote=引用 11 楼 lbxx159753 的回复:] [quote=引用 10 楼 wangjian0228 的回复:] [quote=引用 8 楼 lbxx159753 的回复:] 看for update的字面意思感觉就挺能明白为啥insert的时候加锁了啊,而且应该是表锁,因为mysql不确定你select完了后是否update这条记录的主键,如果你刚好update emp set empid=201;那么自然跟insert的那个冲突了。除非你能事先告诉mysql你select完后只更新102,就只会有行锁了。
next_key 锁会划分锁范围,例子中101--正无穷都加了同一个域的锁,如果更新在1到101中就是行锁了[/quote] 确实是next_key锁导致的。不过感觉有点晕了,行锁和record lock、next_key lock、gap lock等是什么关系,总称吗[/quote] 行锁是一类名称,record lock的实现是行锁 如对已存在id for update,gap lock 是域锁,更新范围数据时会加gap,next_key lock 是gap和record的组合,昨天刚看了官方文档,明确了很多概念[/quote] 多谢指教。我还有个疑问,就是mysql只提供表锁、s和x锁,而数据库引擎(比如Innodb)提供意向锁is和ix这种是吧,那么gap、next_key这种的实际上是在这四种所基础上实现出来的,是这样的吗。感觉各种各样的锁,概念好繁杂,他们之间的关系也很不清晰。同时哪些情况下会加那种锁,这个也很不明白。
致命的西瓜 2016-07-20
  • 打赏
  • 举报
回复
引用 11 楼 lbxx159753 的回复:
[quote=引用 10 楼 wangjian0228 的回复:] [quote=引用 8 楼 lbxx159753 的回复:] 看for update的字面意思感觉就挺能明白为啥insert的时候加锁了啊,而且应该是表锁,因为mysql不确定你select完了后是否update这条记录的主键,如果你刚好update emp set empid=201;那么自然跟insert的那个冲突了。除非你能事先告诉mysql你select完后只更新102,就只会有行锁了。
next_key 锁会划分锁范围,例子中101--正无穷都加了同一个域的锁,如果更新在1到101中就是行锁了[/quote] 确实是next_key锁导致的。不过感觉有点晕了,行锁和record lock、next_key lock、gap lock等是什么关系,总称吗[/quote] 行锁是一类名称,record lock的实现是行锁 如对已存在id for update,gap lock 是域锁,更新范围数据时会加gap,next_key lock 是gap和record的组合,昨天刚看了官方文档,明确了很多概念
juebanxiaobai 2016-07-20
  • 打赏
  • 举报
回复
引用 18 楼 lbxx159753 的回复:
另外,假设当前数据库存在1、2和10三条,当id=5不存在时,insert[3,9](都是闭区间)不行,但是insert 2和10都可以。也就是说没有的用gap锁
例子中使用的是普通索引,如果是主键或者唯一索引的话不会出现Next-Key Lock
juebanxiaobai 2016-07-20
  • 打赏
  • 举报
回复
引用 19 楼 lbxx159753 的回复:
晕死,lz前后两个例子的键不一样。一个主键,一个单纯的key而已。对于uniq类型的主键,就是按我说的那种来加锁的。非uniq就是lz第二个例子的加锁方式
怪我了,确实不一样,应该普通索引,而不是主键
lbxx159753 2016-07-20
  • 打赏
  • 举报
回复
晕死,lz前后两个例子的键不一样。一个主键,一个单纯的key而已。对于uniq类型的主键,就是按我说的那种来加锁的。非uniq就是lz第二个例子的加锁方式
lbxx159753 2016-07-20
  • 打赏
  • 举报
回复
另外,假设当前数据库存在1、2和10三条,当id=5不存在时,insert[3,9](都是闭区间)不行,但是insert 2和10都可以。也就是说没有的用gap锁
lbxx159753 2016-07-20
  • 打赏
  • 举报
回复
引用 15 楼 juebanxiaobai 的回复:
[quote=引用 14 楼 wangjian0228 的回复:] [quote=引用 13 楼 lbxx159753 的回复:] [quote=引用 12 楼 wangjian0228 的回复:] [quote=引用 11 楼 lbxx159753 的回复:] [quote=引用 10 楼 wangjian0228 的回复:] [quote=引用 8 楼 lbxx159753 的回复:] 看for update的字面意思感觉就挺能明白为啥insert的时候加锁了啊,而且应该是表锁,因为mysql不确定你select完了后是否update这条记录的主键,如果你刚好update emp set empid=201;那么自然跟insert的那个冲突了。除非你能事先告诉mysql你select完后只更新102,就只会有行锁了。
next_key 锁会划分锁范围,例子中101--正无穷都加了同一个域的锁,如果更新在1到101中就是行锁了[/quote] 确实是next_key锁导致的。不过感觉有点晕了,行锁和record lock、next_key lock、gap lock等是什么关系,总称吗[/quote] 行锁是一类名称,record lock的实现是行锁 如对已存在id for update,gap lock 是域锁,更新范围数据时会加gap,next_key lock 是gap和record的组合,昨天刚看了官方文档,明确了很多概念[/quote] 多谢指教。我还有个疑问,就是mysql只提供表锁、s和x锁,而数据库引擎(比如Innodb)提供意向锁is和ix这种是吧,那么gap、next_key这种的实际上是在这四种所基础上实现出来的,是这样的吗。感觉各种各样的锁,概念好繁杂,他们之间的关系也很不清晰。同时哪些情况下会加那种锁,这个也很不明白。[/quote] 事务处理中读写锁是基础,不同的数据库引擎封装了读写锁后形成了gap,record...,is 和ix 也是基于s 和 x 为了增加读写锁的判断效率 ,个人学习锁的时候只是理解原理,没有特意去记住XXX锁的名字,而是在具体的业务场景中结合,自然的串起来了[/quote] 问题已经解决: 原因如下: MySQL InnoDB支持三种行锁定方式: 行锁(Record Lock):锁直接加在索引记录上面。 间隙锁(Gap Lock):锁加在不存在的空闲空间,可以是两个索引记录之间,也可能是第一个索引记录之前或者最后一个索引之后的空间。 Next-Key Lock:行锁与间隙锁组合起来用就叫做Next-Key Lock。 默认情况下,InnoDB工作在"可重复读"的隔离情况下,并且以Next-Key Lock的方式对数据进行加锁,这样就可以有效地防止"幻读"的发生。Next-key Lock是行锁与间隙锁的组合,这样,当InnoDB扫描索引记录的时候,会首先对选中的索引记录加上行锁(Record Lock),再对索引记录两边的间隙加上间隙锁(Gap Lock)。如果一个间隙被事务T1加了锁,其他事务是不能在这个间隙插入记录的。 测试所使用到的表: mysql> create table test(id int,key idx_test(id))engine=innodb; Query OK, 0 rows affected (0.01 sec) mysql> insert into test values(1),(3),(5),(11); Query OK, 4 rows affected (0.00 sec) Records: 4 Duplicates: 0 Warnings: 0 为什么session_2上面插入语句出现锁等待的情况?InnoDB是行锁,在session_1里面锁住了a=5的行,其他他应该不受影响。为什么? 分析如下: 分析如下: 因为InnoDB对于行的查询采用了Next-Key Lock的算法,锁定的不是单个值,而是一个范围。上面的索引有1,3,5,11,被Next-Key Locking的区间为: [-无穷,1),[1,3),[3,5),[5,11),[11,+无穷) 下面我们就可以解释上面的出现的问题了: mysql> select * from test where id=5 for update; +------+ | id | +------+ | 5 | +------+ 1 row in set (0.00 sec) 该SQL语句锁定的范围是[3,5),[5],[5,11),所以当我们视图插入5,6,7,8,9,10会被锁住。而插入非这个范围的值都是正常的。[/quote] 我测试了一下你的例子,发现当id=5存在时,3、5、6、10等都可以insert,也就是只有单一个行锁
qq_24044757 2016-07-20
  • 打赏
  • 举报
回复
http://hedengcheng.com/?p=771 这个博客讲的很清楚,楼主可以看看
juebanxiaobai 2016-07-20
  • 打赏
  • 举报
回复
引用 14 楼 wangjian0228 的回复:
[quote=引用 13 楼 lbxx159753 的回复:]
[quote=引用 12 楼 wangjian0228 的回复:]
[quote=引用 11 楼 lbxx159753 的回复:]
[quote=引用 10 楼 wangjian0228 的回复:]
[quote=引用 8 楼 lbxx159753 的回复:]
看for update的字面意思感觉就挺能明白为啥insert的时候加锁了啊,而且应该是表锁,因为mysql不确定你select完了后是否update这条记录的主键,如果你刚好update emp set empid=201;那么自然跟insert的那个冲突了。除非你能事先告诉mysql你select完后只更新102,就只会有行锁了。

next_key 锁会划分锁范围,例子中101--正无穷都加了同一个域的锁,如果更新在1到101中就是行锁了[/quote]
确实是next_key锁导致的。不过感觉有点晕了,行锁和record lock、next_key lock、gap lock等是什么关系,总称吗[/quote]

行锁是一类名称,record lock的实现是行锁 如对已存在id for update,gap lock 是域锁,更新范围数据时会加gap,next_key lock 是gap和record的组合,昨天刚看了官方文档,明确了很多概念[/quote]
多谢指教。我还有个疑问,就是mysql只提供表锁、s和x锁,而数据库引擎(比如Innodb)提供意向锁is和ix这种是吧,那么gap、next_key这种的实际上是在这四种所基础上实现出来的,是这样的吗。感觉各种各样的锁,概念好繁杂,他们之间的关系也很不清晰。同时哪些情况下会加那种锁,这个也很不明白。[/quote]

事务处理中读写锁是基础,不同的数据库引擎封装了读写锁后形成了gap,record...,is 和ix 也是基于s 和 x 为了增加读写锁的判断效率 ,个人学习锁的时候只是理解原理,没有特意去记住XXX锁的名字,而是在具体的业务场景中结合,自然的串起来了[/quote]

问题已经解决:
原因如下:
MySQL InnoDB支持三种行锁定方式:
行锁(Record Lock):锁直接加在索引记录上面。
间隙锁(Gap Lock):锁加在不存在的空闲空间,可以是两个索引记录之间,也可能是第一个索引记录之前或者最后一个索引之后的空间。
Next-Key Lock:行锁与间隙锁组合起来用就叫做Next-Key Lock。
默认情况下,InnoDB工作在"可重复读"的隔离情况下,并且以Next-Key Lock的方式对数据进行加锁,这样就可以有效地防止"幻读"的发生。Next-key Lock是行锁与间隙锁的组合,这样,当InnoDB扫描索引记录的时候,会首先对选中的索引记录加上行锁(Record Lock),再对索引记录两边的间隙加上间隙锁(Gap Lock)。如果一个间隙被事务T1加了锁,其他事务是不能在这个间隙插入记录的。
测试所使用到的表:
mysql> create table test(id int,key idx_test(id))engine=innodb;
Query OK, 0 rows affected (0.01 sec)

mysql> insert into test values(1),(3),(5),(11);
Query OK, 4 rows affected (0.00 sec)
Records: 4 Duplicates: 0 Warnings: 0

为什么session_2上面插入语句出现锁等待的情况?InnoDB是行锁,在session_1里面锁住了a=5的行,其他他应该不受影响。为什么?
分析如下:
分析如下:
因为InnoDB对于行的查询采用了Next-Key Lock的算法,锁定的不是单个值,而是一个范围。上面的索引有1,3,5,11,被Next-Key Locking的区间为:
[-无穷,1),[1,3),[3,5),[5,11),[11,+无穷)
下面我们就可以解释上面的出现的问题了:
mysql> select * from test where id=5 for update;
+------+
| id |
+------+
| 5 |
+------+
1 row in set (0.00 sec)
该SQL语句锁定的范围是[3,5),[5],[5,11),所以当我们视图插入5,6,7,8,9,10会被锁住。而插入非这个范围的值都是正常的。
juebanxiaobai 2016-07-19
  • 打赏
  • 举报
回复
引用 6 楼 zjcxc 的回复:
直接看官网的文档上关于 Next-Key lock 的说明就很清楚了 http://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html
谢大神,这就去看看
zjcxc 2016-07-19
  • 打赏
  • 举报
回复
直接看官网的文档上关于 Next-Key lock 的说明就很清楚了 http://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html
juebanxiaobai 2016-07-19
  • 打赏
  • 举报
回复
引用 4 楼 wangjian0228 的回复:
[quote=引用 3 楼 juebanxiaobai 的回复:] [quote=引用 2 楼 wangjian0228 的回复:] [quote=引用 1 楼 wangjian0228 的回复:] 1: for update 如果指明主键且主键存在则锁行,否则锁表 2:因为建表时没有指定主键,102for update时锁表,所以插入数据时遇到表锁--等待
源数据并没有102,所以应该没有表锁,至于案例插入201为什么会出现等待,建议亲自试一下,是否是真的加了写锁[/quote] 原表是有主键索引的,亲自尝试后确实加锁![/quote] 所以结论for update 对读是行锁,对写是表锁吧[/quote] 这个结论太草率了。 现在的问题主要是: select * from emp where empid = 102 for update; 肯定是加了间隙锁(Next-Key),但是这个间隙锁是加锁102这条记录还是加锁了针对102后续的记录? 从表中的结果可以看出确实是加锁了后续的记录了! 但是后续记录的上限是多少?
致命的西瓜 2016-07-19
  • 打赏
  • 举报
回复
引用 3 楼 juebanxiaobai 的回复:
[quote=引用 2 楼 wangjian0228 的回复:] [quote=引用 1 楼 wangjian0228 的回复:] 1: for update 如果指明主键且主键存在则锁行,否则锁表 2:因为建表时没有指定主键,102for update时锁表,所以插入数据时遇到表锁--等待
源数据并没有102,所以应该没有表锁,至于案例插入201为什么会出现等待,建议亲自试一下,是否是真的加了写锁[/quote] 原表是有主键索引的,亲自尝试后确实加锁![/quote] 所以结论for update 对读是行锁,对写是表锁吧
juebanxiaobai 2016-07-19
  • 打赏
  • 举报
回复
引用 2 楼 wangjian0228 的回复:
[quote=引用 1 楼 wangjian0228 的回复:] 1: for update 如果指明主键且主键存在则锁行,否则锁表 2:因为建表时没有指定主键,102for update时锁表,所以插入数据时遇到表锁--等待
源数据并没有102,所以应该没有表锁,至于案例插入201为什么会出现等待,建议亲自试一下,是否是真的加了写锁[/quote] 原表是有主键索引的,亲自尝试后确实加锁!
lbxx159753 2016-07-19
  • 打赏
  • 举报
回复
引用 10 楼 wangjian0228 的回复:
[quote=引用 8 楼 lbxx159753 的回复:] 看for update的字面意思感觉就挺能明白为啥insert的时候加锁了啊,而且应该是表锁,因为mysql不确定你select完了后是否update这条记录的主键,如果你刚好update emp set empid=201;那么自然跟insert的那个冲突了。除非你能事先告诉mysql你select完后只更新102,就只会有行锁了。
next_key 锁会划分锁范围,例子中101--正无穷都加了同一个域的锁,如果更新在1到101中就是行锁了[/quote] 确实是next_key锁导致的。不过感觉有点晕了,行锁和record lock、next_key lock、gap lock等是什么关系,总称吗
致命的西瓜 2016-07-19
  • 打赏
  • 举报
回复
引用 8 楼 lbxx159753 的回复:
看for update的字面意思感觉就挺能明白为啥insert的时候加锁了啊,而且应该是表锁,因为mysql不确定你select完了后是否update这条记录的主键,如果你刚好update emp set empid=201;那么自然跟insert的那个冲突了。除非你能事先告诉mysql你select完后只更新102,就只会有行锁了。
next_key 锁会划分锁范围,例子中101--正无穷都加了同一个域的锁,如果更新在1到101中就是行锁了
lbxx159753 2016-07-19
  • 打赏
  • 举报
回复
上面的描述有错误,只更新102非主键的部分
lbxx159753 2016-07-19
  • 打赏
  • 举报
回复
看for update的字面意思感觉就挺能明白为啥insert的时候加锁了啊,而且应该是表锁,因为mysql不确定你select完了后是否update这条记录的主键,如果你刚好update emp set empid=201;那么自然跟insert的那个冲突了。除非你能事先告诉mysql你select完后只更新102,就只会有行锁了。
致命的西瓜 2016-07-18
  • 打赏
  • 举报
回复
引用 1 楼 wangjian0228 的回复:
1: for update 如果指明主键且主键存在则锁行,否则锁表 2:因为建表时没有指定主键,102for update时锁表,所以插入数据时遇到表锁--等待
源数据并没有102,所以应该没有表锁,至于案例插入201为什么会出现等待,建议亲自试一下,是否是真的加了写锁
加载更多回复(1)

56,687

社区成员

发帖
与我相关
我的任务
社区描述
MySQL相关内容讨论专区
社区管理员
  • MySQL
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧