Ⅰ、InnoDB锁算法的介绍

首先明确一点,锁锁住的是什么?锁锁住的是索引

  • Record Lock
    单个行记录上的锁
  • Gap Lock
    锁定一个范围,但不包含记录本身
  • Next-key Lock
    Gap Lock + Record Lock 锁定一个范围,并且锁定记录本身

Ⅱ、模拟加锁场景

(root@localhost) [test]> desc l;
+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| a     | int(11) | NO   | PRI | NULL    |       |
| b     | int(11) | YES  | MUL | NULL    |       |
| c     | int(11) | YES  | UNI | NULL    |       |
| d     | int(11) | YES  |     | NULL    |       |
+-------+---------+------+-----+---------+-------+
4 rows in set (0.00 sec)(root@localhost) [test]> select * from l;
+---+------+------+------+
| a | b    | c    | d    |
+---+------+------+------+
| 2 |    4 |    6 |    8 |
| 4 |    6 |    8 |   10 |
| 6 |    8 |   10 |   12 |
| 8 |   10 |   12 |   14 |
+---+------+------+------+
4 rows in set (0.02 sec)(root@localhost) [test]> begin;
Query OK, 0 rows affected (0.00 sec)(root@localhost) [test]> select * from l where a = 2 for update;
+---+------+------+------+
| a | b    | c    | d    |
+---+------+------+------+
| 2 |    4 |    6 |    8 |
+---+------+------+------+
1 row in set (0.03 sec)对主键为2的这条记录加锁,这里可以表示三个意思
①record lock:对2加X锁②gap lock:对(负无穷,2)加X锁thd1:hold 2 x gapthd2:hold 2 x record上面两个是兼容的,也就是说,thd2直接操作2这条记录是可以操作的,不需要等待thd3:insert 1,这个线程就要wait,因为1在这个范围内③next-key lock 锁住(负无穷,2] oralce中只有record lock,没有别的意思

一般来说,此处我们根据不同事务隔离级别来分析这个加锁情况如下:

  • rc
    所有某条记录的加锁都是record锁,所有insert不用等待,并发度更好
    --->lock_mode X locks rec but not gap
  • rr
    所有对某条记录加锁都用的next-key locking,insert 并行性能或许有点差
    --->lock_mode X

特殊情况:
会把加锁模式优化为record lock,前提是锁住的那个index是unique的,并且只返回(锁住)一条记录

(a,b)复合索引,查a=? 用的还是next-key locking,查a=?,b=?就会用record lock

Ⅲ、正儿八经的分析几个场景看看

3.1 对主键加锁

(root@localhost) [test]> show variables like 'tx_isolation';
+---------------+-----------------+
| Variable_name | Value           |
+---------------+-----------------+
| tx_isolation  | REPEATABLE-READ |
+---------------+-----------------+
1 row in set (0.01 sec)(root@localhost) [test]> begin;
Query OK, 0 rows affected (0.00 sec)(root@localhost) [test]> select * from l where a <=2 for update;
+---+------+------+------+
| a | b    | c    | d    |
+---+------+------+------+
| 2 |    4 |    6 |    8 |
+---+------+------+------+
1 row in set (0.01 sec)(root@localhost) [test]> show engine innodb status\G
...
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 31220336, ACTIVE 16 sec
2 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 416, OS thread handle 139830453040896, query id 5627 localhost root starting
show engine innodb status
TABLE LOCK table `test`.`l` trx id 31220336 lock mode IX
RECORD LOCKS space id 1358 page no 3 n bits 72 index PRIMARY of table `test`.`l` trx id 31220336 lock_mode X
Record lock, heap no 2 PHYSICAL RECORD: n_fields 6; compact format; info bits 00: len 4; hex 80000002; asc     ;;1: len 6; hex 000001c1b939; asc      9;;2: len 7; hex e0000001a80110; asc        ;;3: len 4; hex 80000004; asc     ;;4: len 4; hex 80000006; asc     ;;5: len 4; hex 80000008; asc     ;;Record lock, heap no 3 PHYSICAL RECORD: n_fields 6; compact format; info bits 00: len 4; hex 80000004; asc     ;;1: len 6; hex 000001c1b93a; asc      :;;2: len 7; hex e1000001a90110; asc        ;;3: len 4; hex 80000006; asc     ;;4: len 4; hex 80000008; asc     ;;5: len 4; hex 8000000a; asc     ;;
...

按道理我们锁住的应该是(负无穷,2],但实际上锁住的范围已经到了4这条记录,此时插入3是插不进去的,为什么?

为了保证解决幻读,要把2到它后面这条记录4这段范围锁住,这时候如果新插入一个2,在原来的2后面是插不进来的,如果4不锁住,新开一个线程可以删除4,又可以新插入一个4

rc的话就是只锁住记录本身,如下:

(root@localhost) [(none)]> show variables like 'tx_isolation';
+---------------+----------------+
| Variable_name | Value          |
+---------------+----------------+
| tx_isolation  | READ-COMMITTED |
+---------------+----------------+
1 row in set (0.00 sec)(root@localhost) [(none)]> begin;
Query OK, 0 rows affected (0.00 sec)(root@localhost) [test]> select * from l where a <=2 for update;
+---+------+------+------+
| a | b    | c    | d    |
+---+------+------+------+
| 2 |    4 |    6 |    8 |
+---+------+------+------+
1 row in set (0.00 sec)(root@localhost) [test]> show engine innodb status\G
...
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 31220337, ACTIVE 6 sec
2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 443, OS thread handle 139830452774656, query id 5649 localhost root starting
show engine innodb status
TABLE LOCK table `test`.`l` trx id 31220337 lock mode IX
RECORD LOCKS space id 1358 page no 3 n bits 72 index PRIMARY of table `test`.`l` trx id 31220337 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 6; compact format; info bits 00: len 4; hex 80000002; asc     ;;1: len 6; hex 000001c1b939; asc      9;;2: len 7; hex e0000001a80110; asc        ;;3: len 4; hex 80000004; asc     ;;4: len 4; hex 80000006; asc     ;;5: len 4; hex 80000008; asc     ;;
...

唯一索引和主键情况一样

3.2 对二级索引加锁

先看rc事务隔离级别

(root@localhost) [test]> begin;
Query OK, 0 rows affected (0.00 sec)(root@localhost) [test]> select * from l where b = 6 for update;
+---+------+------+------+
| a | b    | c    | d    |
+---+------+------+------+
| 4 |    6 |    8 |   10 |
+---+------+------+------+
1 row in set (0.02 sec(root@localhost) [test]> show engine innodb status\G
...
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 31220338, ACTIVE 35 sec
3 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 443, OS thread handle 139830452774656, query id 5653 localhost root starting
show engine innodb status
TABLE LOCK table `test`.`l` trx id 31220338 lock mode IX
RECORD LOCKS space id 1358 page no 5 n bits 72 index b of table `test`.`l` trx id 31220338 lock_mode X locks rec but not gap
Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 00: len 4; hex 80000006; asc     ;;1: len 4; hex 80000004; asc     ;;RECORD LOCKS space id 1358 page no 3 n bits 72 index PRIMARY of table `test`.`l` trx id 31220338 lock_mode X locks rec but not gap
Record lock, heap no 3 PHYSICAL RECORD: n_fields 6; compact format; info bits 00: len 4; hex 80000004; asc     ;;1: len 6; hex 000001c1b93a; asc      :;;2: len 7; hex e1000001a90110; asc        ;;3: len 4; hex 80000006; asc     ;;4: len 4; hex 80000008; asc     ;;5: len 4; hex 8000000a; asc     ;;
...

先对二级索引b加record锁:lock_mode X locks rec but not gap锁住了(6,4),6是二级索引,4是主键值

再对聚集索引加锁也是record locks,锁聚集索引index primary,锁住了a=4

再分析rr隔离级别下的情况,如下:

(root@localhost) [test]> show variables like 'tx_isolation';
+---------------+-----------------+
| Variable_name | Value           |
+---------------+-----------------+
| tx_isolation  | REPEATABLE-READ |
+---------------+-----------------+
1 row in set (0.00 sec)(root@localhost) [test]> begin;
Query OK, 0 rows affected (0.00 sec)(root@localhost) [test]> select * from l where b = 6 for update;
+---+------+------+------+
| a | b    | c    | d    |
+---+------+------+------+
| 4 |    6 |    8 |   10 |
+---+------+------+------+
1 row in set (0.01 sec)(root@localhost) [test]> show engine innodb status\G
...
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 31220340, ACTIVE 5 sec
4 lock struct(s), heap size 1136, 3 row lock(s)
MySQL thread id 444, OS thread handle 139830446065408, query id 5673 localhost root starting
show engine innodb status
TABLE LOCK table `test`.`l` trx id 31220340 lock mode IX
RECORD LOCKS space id 1358 page no 5 n bits 72 index b of table `test`.`l` trx id 31220340 lock_mode X
Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 00: len 4; hex 80000006; asc     ;;1: len 4; hex 80000004; asc     ;;RECORD LOCKS space id 1358 page no 3 n bits 72 index PRIMARY of table `test`.`l` trx id 31220340 lock_mode X locks rec but not gap
Record lock, heap no 3 PHYSICAL RECORD: n_fields 6; compact format; info bits 00: len 4; hex 80000004; asc     ;;1: len 6; hex 000001c1b93a; asc      :;;2: len 7; hex e1000001a90110; asc        ;;3: len 4; hex 80000006; asc     ;;4: len 4; hex 80000008; asc     ;;5: len 4; hex 8000000a; asc     ;;RECORD LOCKS space id 1358 page no 5 n bits 72 index b of table `test`.`l` trx id 31220340 lock_mode X locks gap before rec
Record lock, heap no 4 PHYSICAL RECORD: n_fields 2; compact format; info bits 00: len 4; hex 80000008; asc     ;;1: len 4; hex 80000006; asc     ;;
...

这个就稍微有点复杂了,依稀可以看到是加了三个锁,我们挨个分析一波

  • 第一个锁锁住索引b(4,6],next-key lock锁 lock_mode X
  • 第二个锁是对主键a=4这条唯一记录的主键上加一个记录锁(因为唯一),lock_mode X locks rec but not gap
  • 第三个锁是gap before rec 锁住了b(6,8),也就是对8加了gap

为什么要锁住(6,8)?

假设不锁住这块,一个线程插入了(3,6),只锁住(4,6]那就可以插入了,那原来的线程第一次返回的只有一条b=6的记录,那第二次执行就出现了两条b=6,就幻读了

tips:
新插入的6是在(6,8)这个范围里的,新插入的相同的记录,都在已存在的记录后面 4 6 6(新插) 8

转载于:https://www.cnblogs.com/---wunian/p/9173512.html

InnoDB中锁的算法(1)相关推荐

  1. 秒懂INNODB的锁

    今天我们来聊聊MySQL中InnoDB存储引擎的锁. 锁是数据库系统系统区别于文件系统的一个关键特性. lock和 latch latch latch在MySQL中是用来保证并发多线程操作操作临界资源 ...

  2. MySQL探秘(七):InnoDB行锁算法

     在上一篇<InnoDB一致性非锁定读>中,我们了解到InnoDB使用一致性非锁定读来避免在一般的查询操作(SELECT FOR UPDATE等除外)时使用锁.然而锁这个事情是无法避免的, ...

  3. mysql innodb禁用事务_MySQL InnoDB事务中锁问题(三)

    试想,事务如果都是串行的,那么就不需要锁了,但是性能肯定没法接受.加锁只是为了提高事务并行度,并且解决并发事务执行过程中引起的脏写.脏读.不可重复读.幻读这些问题的一种解决方案(MVCC算是一种解决脏 ...

  4. 一文了解Innodb中的锁

    对于大部分的后端开发来说,数据库尤其是MySQL是一个离不开的知识点,那么今天就分享一下最近学习的数据库中的锁相关知识,并以此解释事务隔离性问题. 如下是整理的Mysql中锁的相关知识点 什么是锁 锁 ...

  5. mysql临键锁_详解 MySql InnoDB 中的三种行锁(记录锁、间隙锁与临键锁)

    详解 MySql InnoDB 中的三种行锁(记录锁.间隙锁与临键锁) 前言 InnoDB 通过 MVCC 和 NEXT-KEY Locks,解决了在可重复读的事务隔离级别下出现幻读的问题.MVCC  ...

  6. MySQL怎么运行的系列(十)Innodb中的锁:记录锁、临键锁、间隙锁、意向锁

    本系列文章目录 展开/收起 MySQL怎么运行的系列(一)mysql体系结构和存储引擎 MySQL怎么运行的系列(二)Innodb缓冲池 buffer pool 和 改良版LRU算法 Mysql怎么运 ...

  7. MySQL/InnoDB中,乐观锁、悲观锁、共享锁、排它锁、行锁、表锁、死锁概念的理解

    MySQL/InnoDB的加锁,一直是一个面试中常问的话题.例如,数据库如果有高并发请求,如何保证数据完整性?产生死锁问题如何排查并解决?我在工作过程中,也会经常用到,乐观锁,排它锁,等.于是今天就对 ...

  8. innodb中master线程的调度的算法改进(mysql 5.6.26)

    innodb中master线程的调度的算法改进(mysql 5.6.26)作者:周琳QQ:715169549源码交流群:1963809051.master线程的调度:/**************** ...

  9. mysql innodb 间隙锁_MySQL中InnoDB的间隙锁问题

    在为一个客户排除死锁问题时我遇到了一个有趣的包括InnoDB间隙锁的情形.对于一个WHERE子句不匹配任何行的非插入的写操作中,我预期事务应该不会有锁,但我错了.让我们看一下这张表及示例UPDATE. ...

最新文章

  1. boost::local_time模块实现自纪元以来的秒数的测试程序
  2. Jetty在win10上的配置,IDEA中配置Jetty,Maven中配置Jetty插件,Eclipse中配置Jetty插件及其使用,通过java代码内嵌Jetty Server
  3. java注册登录客户端_GitHub - a-voyager/LoginSystem_Client: 登录注册系统(桌面客户端)——Java课程实践...
  4. HDS:转型关键还是私有云
  5. mysql 批量添加字段前缀_MySQL中批量前缀表的sql语句
  6. SetWindowHookEx 做消息响应
  7. docbook_DocBook简介,一种值得学习的灵活标记语言
  8. LaTex ——P2 源文件的基本结构
  9. 【RobotStudio学习笔记】(三)程序创建
  10. linux read函数段错误,linux C++ 莫名奇异的段错误(segmentation fault),无法调用其他函数...
  11. 5ecsgo正在发送客户端_MQTT X 桌面客户端使用指南
  12. 大班音乐机器人反思_大班音乐活动机器人
  13. 【原创】VBA学习笔记(313)VBA字典相关:遍历字典,用key查item, 用item查key的方法
  14. 谈谈应聘阿里全流程(良心之作,好评满满)
  15. 2020博客之星年度总评选 - 显示排名
  16. defineExpose暴露
  17. linux实现进度条
  18. 微信小程序注册入口和注册流程(完整版图文教程)
  19. 商业智能,数据仓库,ETL,数仓调度工具informatica介绍手账(三)
  20. 在数据为王的人工智能时代如何收集机器学习数据

热门文章

  1. 【王道操作系统笔记】操作系统的概念,功能和目标
  2. Helix Streaming Server 简单配置
  3. OpenCV 实现颜色直方图
  4. java执行脚本命令(shell脚本或cmd脚本)
  5. Mybatis数据库连接报错:对实体 “characterEncoding“ 的引用必须以 ‘;‘ 分隔符结尾
  6. tcp连接时,BROKEN PIPE错误
  7. LeetCode 热题 HOT 100 完整题解笔记知识点分类 C++代码实现
  8. 将html对象转换成jq,2js对象与jq对象之间互转.html
  9. php 单例 重连,PHP单例模式详解
  10. SpringBoot→thymeleaf静态模板