innodb next-key lock解析

參考http://blog.csdn.net/zbszhangbosen/article/details/7434637#reply

这里补充一些:

(1)InnoDB默认加锁方式是next-key locking

(2)在聚集索引中,假设主键有唯一性约束(unique,auto increment),next-key locking 会自己主动降级为record locking。

(3)因为事务的隔离性和一致性要求,会对全部扫描到的record加锁。

比方:update ... where/delete .. where/select ...from...lock in share mode/ select .. from .. for update这都是next-key lock。

(4)注意优化器的选择。

包含聚集索引和辅助索引。有时会用全表扫描替代索引扫描。这时整张表(聚集索引表)都会被加锁。

record lock:记录锁,也就是只锁着单独的一行

gap lock:区间锁。只锁住一个区间(注意这里的区间都是开区间。也就是不包含边界值,至于为什么这么定义?innodb官方定义的)
next-key lock:record lock+gap lock,所以next-key lock也就半开半闭区间,且是下界开,上界闭。(为什么这么定义?innodb官方定义的)
以下来举个手冊上的样例看什么是next-key lock。

假如一个索引的行有10,11,13,20
那么可能的next-key lock的包含:
(无穷小, 10]
(10,11]
(11,13]
(13,20]
(20, 无穷大) (这里无穷大为什么不是闭合?你数学不到家~~)
好了如今通过举样例说明:


表test
mysql> show create table test;
+-------+--------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                           |
+-------+--------------------------------------------------------------------------------------------------------+
| test  | CREATE TABLE `test` (
  `a` int(11) NOT NULL,
  PRIMARY KEY (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+-------+--------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> select * from test;
+----+
| a  |
+----+
| 11 |
| 12 |
| 13 |
| 14 |
+----+
4 rows in set (0.00 sec)
開始实验:
(一)
session 1:
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)


mysql> delete from test where a=11;
Query OK, 1 row affected (0.00 sec)


session 2:
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)


mysql> insert into test values(10);
Query OK, 1 row affected (0.00 sec)


mysql> insert into test values(15);
Query OK, 1 row affected (0.00 sec)


mysql> insert into test values(9);
Query OK, 1 row affected (0.00 sec)


mysql> insert into test values(16);
Query OK, 1 row affected (0.01 sec)


mysql> rollback;
Query OK, 0 rows affected (0.00 sec)


ok,上面的情况是预期的。由于a上有索引。那么当然就仅仅要锁定一行,所以其它行的插入不会被堵塞。


那么接下来的情况就有意思了
(二)
session 1(跟上一个session 1同样):
delete from test where a=22;
Query OK, 0 rows affected (0.01 sec)


session 2:
mysql> insert into test values (201);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into test values (20);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into test values (19);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into test values (18);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into test values (16);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into test values (9);
Query OK, 1 row affected (0.00 sec)


从上面的结果来看,在a=11后面全部的行,也就是区间(11,无穷大)都被锁定了。先不解释原因,再来看一种情况:
(三)
session 1:
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)


mysql> select * from test;
+----+
| a  |
+----+
|  7 |
|  9 |
| 10 |
| 12 |
| 13 |
| 14 |
| 15 |
| 22 |
| 23 |
| 24 |
| 25 |
+----+
11 rows in set (0.00 sec)


mysql> delete from test where a=21;
Query OK, 0 rows affected (0.00 sec)


session 2:
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)


mysql> insert into test values (20);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into test values (26);
Query OK, 1 row affected (0.00 sec)


mysql> insert into test values (21);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into test values (16);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into test values (6);
Query OK, 1 row affected (0.01 sec)


从这里能够看出。如今被锁住的区间就仅仅有[16,21)了。
有了前面对三种类型的加锁解释,如今能够来解释为什么会这样了,在innodb表中 delete from where ..针对扫描到的索引记录加next-key锁(详细的什么语句加什么锁能够查看手冊,另外须要说明一下。行锁加锁对象永远是索引记录,由于innodb中表即索引) 。
在(一)中。实际上加的next-key lock就是(11,11] 因此也仅仅有a=11这一条记录被锁住。其它全部插入都没有关系。
在(二)中,由于a=22这条记录不存在,并且22比表里全部的记录值都大,所以在innodb看来锁住的区间就是(14, 无穷大)。

所以在插入14以后的值都提示被锁住,而14之前的则能够。


在(三)种。a=21也是不存在,可是在表里面21前后都有记录。因此这里next-key lock的区间也就是(15,21],因此不在这个区间内的都能够插入。
那么为什么next-key lock都是下界开区间。上界闭区间呢?这个倒不重要,管它呢,可是有一点我个人却认为比較怪,比方说
delete test where a > 11           #------- 1
它的next-key lock是(11, 无穷大) 
delete test where a < 11           #------- 2
它的next-key lock是(无穷小, 10]
这样给人的感觉就非常怪。由于在手冊上对next-key lock的定义:
Next-key lock: This is a combination of a record lock on the index record and a gap lock on the gapbefore the index record.
而在1那种情况下,如果依照手冊上的解释,记录锁和它之前的gap那么就会有些牵强。

[今天再次看了一遍官方手冊,是之前自己的理解不到位,这个before是对的,由于innodb在加锁时是全部扫描过程中遇到的记录都会被加锁,那么对于1那种情况,实际上是从12開始扫描,可是由于要保证a>11的都被delete掉。因此得一直扫描下去那自然最大值就是无穷大,由于这个next-key lock就是无穷大这条记录(这是如果的一条记录,表示一个边界)加上它之前的gap lock (11, 无穷大),所以在不论什么时候next-lock都是record lock加上这个record之前的一个gap lock]
可是仅仅要我们自己能理解即可了:记录锁---锁单条记录。区间锁---锁一个开区间。next-key 锁---前面两者的结合,而不要管什么before。
另外next-key lock尽管在非常多时候是锁一个区间,但要明确一个区间也可能仅仅有一个元素。因此在称delete from tb where key=x 这样的情况下加next-key锁也是全然正确的。




另外还提两点:
1.假设我们的SQL语句里面没有利用到索引。那么加锁对象将是全部行(但不是加表锁)。所以建索引是非常重要的
2.next-key lock是为防止幻读的发生,而仅仅有repeatable-read以及以上隔离级别才干防止幻读。所以在read-committed隔离级别以下没有next-key lock这一说法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值