1
RedisMasterNode 2020-03-05 22:17:39 +08:00
id 是唯一索引的时候没有使用 next_key locking,应该是 record lock
|
2
jdz OP @RedisMasterNode 你可以试一下,此时插入 2 也会失败
|
3
simonlu9 2020-03-06 09:18:32 +08:00
很简单,正常会锁,因为在可重复读的情况下,在开启事务中,select * from test_table where id = 3 for update 这一句第一次查询是没有数据,第二次查询我也要没有数据,那么怎么保证,只能开间歇锁范围,范围是多少,决定在于你 id=3 这个记录的索引范围,假如 id 没有索引,肯定会锁全表
|
5
hhyvs111 2020-03-06 09:33:18 +08:00
第三行不存在,所以要锁一定的范围。如果一个条件无法通过索引快速过滤,那么存储引擎层面就会将所有记录加锁后返回,然后由 MySQL Server 层进行过滤,因此也就把所有的记录都锁上了。
|
6
RedisMasterNode 2020-03-06 09:42:07 +08:00 via Android
@jdz 你的描述很模糊,id=3 这行存在?
|
7
RedisMasterNode 2020-03-06 09:43:51 +08:00 via Android
@RedisMasterNode 抱歉是我眼睛很模糊(滑跪!)如果按照这个描述的话不存在 3 这行当然会进行范围锁 orz
|
8
simonlu9 2020-03-06 09:56:33 +08:00
@jdz 3 这一行根本不存在,在保持可重复读的情况下,是不允许其他事务插入 3 这一行的操作,由于你存在 3 这一行,所以要锁范围,至于范围是多少,是采取于你的记录
|
10
sanggao 2020-03-06 10:01:22 +08:00
怎么证明使用了间隙所
|
11
zifangsky 2020-03-06 10:33:39 +08:00
Next-Key 锁:当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB 会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙( GAP )”,InnoDB 也会对这个“间隙”加锁,这种锁机制就是所谓的 Next-Key 锁。
举例来说,假如 emp 表中只有 101 条记录,其 empid 的值分别是 1、2、…、100、101,下面的 SQL: Select * from emp where empid > 100 for update; 是一个范围条件的检索,InnoDB 不仅会对符合条件的 empid 值为 101 的记录加锁,也会对 empid 大于 101 (这些记录并不存在)的“间隙”加锁。 同样,InnoDB 除了通过范围条件加锁时使用 Next-Key 锁外,如果使用相等条件请求给一个不存在的记录加锁,InnoDB 也会使用 Next-Key 锁! ——《深入浅出 MySQL 》 至于为什么要设置这种这种机制,坐等其他人回答 |
12
sujin190 2020-03-06 10:53:51 +08:00
既然锁是和记录关联的,你都说了这行不存在,那如何给这一行加锁呢,虽然我房子还没建但是你先给我上个锁的意思??
|
13
BBCCBB 2020-03-06 11:06:48 +08:00
就是因为没有 id=3, 才会把比 3 小的第一个 id 和比 3 大的第 1 个 id 这个区间锁起来, 防止插入 id=3 的.
|
14
fancy111 2020-03-06 11:14:04 +08:00
楼上正确
|
15
gaius 2020-03-06 11:45:09 +08:00 via Android
id 是主键,因为你 for update 了,所以用的 gap,否则是行。
|
16
gaius 2020-03-06 11:52:33 +08:00 via Android
我错了..上面我瞎说的 原来是不存在
|
18
chibupang 2020-03-06 13:11:20 +08:00 via iPhone
因为数据库引擎无法判断你当前的事务需要对 id=3 的字段做什么操作。即使这个记录不存在,但是当前事务的后续操作可能是插入一条 id=3 的记录,如果是这样,必然是介于 id=2 到 id=4 之间,如果不范围锁定的话会出现什么情况?可能另一事务也正在插入一条 id=3 的记录,这样就会出现两条不同的 id=3 记录。
|
19
lhx2008 2020-03-06 14:06:57 +08:00 via Android
楼主问的是为什么要多锁,那是因为锁只能挂在有数据的地方
如果现在一个只有 1 4,那就是 1-4 之间间隙被锁,无法插任何数 |
20
DonaldY 2020-03-06 14:15:49 +08:00
@jdz 你 id = 2 的记录原本就没有吧,所以插入会失败。
验证了下,当`SELECT FOR UPDATE` id 不存在的,会锁住其他未插入。 |
21
BBCCBB 2020-03-06 14:36:21 +08:00
@jdz 因为他要锁索引, 而 id=3 的索引目前不存在, 不能直接锁 id=3 的索引, 所以他就锁区间来解决 这个问题...
|
22
Aresxue 2020-03-07 14:26:17 +08:00
间隙锁是为防止幻读,因为 id=3 不存在所以要锁定区间,如果存在就是行锁了
|
23
jsdi 2022-03-13 15:07:54 +08:00
我的理解是因为这是当前读,当前读不仅要对当前记录加行锁,还要对邻近记录加间隙锁防止幻读
当前读:读取最新版本数据,除了 for update ,还有 insert 、update 、delete 都是当前读 |
24
cocong 2022-03-24 17:50:42 +08:00
因为锁是加在索引上的,但是 id=3 这条记录不存在,当然也就不存在这个索引了,那你说怎么加?只能让未来可能插入索引的那个位置 的那个间隙的两边的索引来帮忙了。这样其它事务想新增 id=3 的记录时,就得先问问旁边那两位索引兄弟:“朋友,这里没人坐吧?”,答:“有人做了”。这不,就添加不了了。
|
25
cocong 2022-03-24 17:52:53 +08:00
前面的问法不合适,感觉这么问更准确些:“朋友,我能过来挤一挤吗?”,答:“不好意思,我俩被锁住了!”
|