zoukankan      html  css  js  c++  java
  • 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这一说法。

  • 相关阅读:
    交换函数swap的三种实现方法
    oracle如何修改某一列的数据类型
    安装 kibana 以及添加中文分词器
    linux 安装Elasticsearch
    docker添加tomcat容器成功无法访问首页
    docker run-it centos提示FATAL
    启动、重新启动容器后,进入交互模式
    获取阿里云docker加速器地址
    CentOS6 修改默认字符集为GBK
    linux中如何查看redis的版本
  • 原文地址:https://www.cnblogs.com/llguanli/p/7115523.html
Copyright © 2011-2022 走看看