• MySQL select加锁分析


    InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点MySQL与Oracle不同,后者是通过在数据块中对相应数据行加锁来实现的。 InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!

    下面讨论是基于InnoDB引擎、事务隔离级别:可重复读和串行化

    表数据如下,pId为主键索引

    pId(int)name(varchar)num(int)
    1aaa100
    2bbb200
    7ccc200

    RR/Serializable+条件列非索引

    RR级别需要多考虑的就是gap lock,他的加锁特征在于,无论你怎么查都是锁全表。如下所示
    接下来分析开始
    (1)select * from table where num = 200
    在RR级别下,不加任何锁,是快照读。
    在Serializable级别下,在pId = 1,2,3,7(全表所有记录)的聚簇索引上加S锁。并且在
    聚簇索引的所有间隙(-∞,1)(1,2)(2,3)(3,7)(7,+∞)加gap lock
    (2)select * from table where num > 200
    在RR级别下,不加任何锁,是快照读。
    在Serializable级别下,在pId = 1,2,3,7(全表所有记录)的聚簇索引上加S锁。并且在
    聚簇索引的所有间隙(-∞,1)(1,2)(2,3)(3,7)(7,+∞)加gap lock
    (3)select * from table where num = 200 lock in share mode
    在pId = 1,2,3,7(全表所有记录)的聚簇索引上加S锁。并且在
    聚簇索引的所有间隙(-∞,1)(1,2)(2,3)(3,7)(7,+∞)加gap lock
    (4)select * from table where num > 200 lock in share mode
    在pId = 1,2,3,7(全表所有记录)的聚簇索引上加S锁。并且在
    聚簇索引的所有间隙(-∞,1)(1,2)(2,3)(3,7)(7,+∞)加gap lock
    (5)select * from table where num = 200 for update
    在pId = 1,2,3,7(全表所有记录)的聚簇索引上加X锁。并且在
    聚簇索引的所有间隙(-∞,1)(1,2)(2,3)(3,7)(7,+∞)加gap lock
    (6)select * from table where num > 200 for update
    在pId = 1,2,3,7(全表所有记录)的聚簇索引上加X锁。并且在
    聚簇索引的所有间隙(-∞,1)(1,2)(2,3)(3,7)(7,+∞)加gap lock

    RR/Serializable+条件列是聚簇索引

    该情况的加锁特征在于,如果where后的条件为精确查询(=的情况),那么只存在record lock。如果where后的条件为范围查询(><的情况),那么存在的是record lock+gap lock。
    (1)select * from table where pId = 2
    在RR级别下,不加任何锁,是快照读。
    在Serializable级别下,是当前读,在pId=2的聚簇索引上加S锁,不存在gap lock。
    (2)select * from table where pId > 2
    在RR级别下,不加任何锁,是快照读。
    在Serializable级别下,是当前读,在pId=3,7的聚簇索引上加S锁。在(2,3)(3,7)(7,+∞)加上gap lock
    (3)select * from table where pId = 2 lock in share mode
    是当前读,在pId=2的聚簇索引上加S锁,不存在gap lock。
    (4)select * from table where pId > 2 lock in share mode
    是当前读,在pId=3,7的聚簇索引上加S锁。在(2,3)(3,7)(7,+∞)加上gap lock
    (5)select * from table where pId = 2 for update
    是当前读,在pId=2的聚簇索引上加X锁。
    (6)select * from table where pId > 2 for update
    在pId=3,7的聚簇索引上加X锁。在(2,3)(3,7)(7,+∞)加上gap lock
    (7)select * from table where pId = 6 [lock in share mode|for update]
    注意了,pId=6是不存在的列,这种情况会在(3,7)上加gap lock。
    (8)select * from table where pId > 18 [lock in share mode|for update]
    注意了,pId>18,查询结果是空的。在这种情况下,是在(7,+∞)上加gap lock。

    RR/Serializable+条件列是非聚簇索引

    这里非聚簇索引,需要区分是否为唯一索引。因为如果是非唯一索引,间隙锁的加锁方式是有区别的。
    先说一下,唯一索引的情况。如果是唯一索引,情况和RR/Serializable+条件列是聚簇索引类似,唯一有区别的是:这个时候有两棵索引树,加锁是加在对应的非聚簇索引树和聚簇索引树上!大家可以自行推敲!
    下面说一下,非聚簇索引是非唯一索引的情况,他和唯一索引的区别就是通过索引进行精确查询以后,不仅存在record lock,还存在gap lock。而通过唯一索引进行精确查询后,只存在record lock,不存在gap lock。老规矩在num列建立非唯一索引
    (1)select * from table where num = 200
    在RR级别下,不加任何锁,是快照读。
    在Serializable级别下,是当前读,在pId=2,7的聚簇索引上加S锁,在num=200的非聚集索引上加S锁,在(100,200)(200,300)加上gap lock。
    (2)select * from table where num > 200
    在RR级别下,不加任何锁,是快照读。
    在Serializable级别下,是当前读,在pId=3的聚簇索引上加S锁,在num=300的非聚集索引上加S锁。在(200,300)(300,+∞)加上gap lock
    (3)select * from table where num = 200 lock in share mode
    是当前读,在pId=2,7的聚簇索引上加S锁,在num=200的非聚集索引上加S锁,在(100,200)(200,300)加上gap lock。
    (4)select * from table where num > 200 lock in share mode
    是当前读,在pId=3的聚簇索引上加S锁,在num=300的非聚集索引上加S锁。在(200,300)(300,+∞)加上gap lock。
    (5)select * from table where num = 200 for update
    是当前读,在pId=2,7的聚簇索引上加S锁,在num=200的非聚集索引上加X锁,在(100,200)(200,300)加上gap lock。
    (6)select * from table where num > 200 for update
    是当前读,在pId=3的聚簇索引上加S锁,在num=300的非聚集索引上加X锁。在(200,300)(300,+∞)加上gap lock
    (7)select * from table where num = 250 [lock in share mode|for update]
    注意了,num=250是不存在的列,这种情况会在(200,300)上加gap lock。
    (8)select * from table where num > 400 [lock in share mode|for update]
    注意了,pId>400,查询结果是空的。在这种情况下,是在(400,+∞)上加gap lock。

  • 相关阅读:
    Go语学习笔记 - gorm使用 - 数据库配置、表新增 Web框架Gin(七)
    门罗币隐私保护之隐形地址
    科技互联网领域,怎样才算突破性创新?
    第十一章-用户进程
    CSS笔记-狂神
    linux下g++链接动态库和静态库
    微软 Edge “不务正业”,新功能遭用户抵制:“你是在抢钱吧?”
    Linux 如何安装Mysql8.X(详细教程)
    机器学习-贝叶斯分类器(附Python代码)
    代码随想录 | Day51
  • 原文地址:https://blog.csdn.net/weixin_35973945/article/details/126143478