Ⅰ、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 gap
thd2: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 0
0: 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 0
0: 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 0
0: 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 0
0: 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 0
0: 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 0
0: 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 0
0: 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 0
0: 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

InnoDB中锁的算法(1)的更多相关文章

  1. InnoDB中锁的算法(2)

    Ⅰ.上节回顾 session1: (root@localhost) [test]> select * from l; +---+------+------+------+ | a | b | c ...

  2. InnoDB中锁的算法(3)

    Ⅰ.隐式锁vs显示锁 session1: (root@localhost) [test]> show variables like 'tx_isolation'; +-------------- ...

  3. InnoDB中锁的模式,锁的查看,算法

    InnoDB中锁的模式   Ⅰ.总览 S行级共享锁lock in share mode X行级排它锁增删改 IS意向共享锁 IX意向排他锁 AI自增锁 Ⅱ.锁之间的兼容性 兼 X IX S IS X ...

  4. InnoDB中锁的查看

    Ⅰ. show engine innodb status\G 1.1 实力分析一波 锁介绍的那篇中已经提到了这个命令,现在我们开一个参数,更细致的分析一下这个命令 (root@localhost) [ ...

  5. InnoDB中锁的模式

    Ⅰ.总览 S行级共享锁 lock in share mode X行级排它锁 增删改 IS意向共享锁 IX意向排他锁 AI自增锁 Ⅱ.锁之间的兼容性 兼 X IX S IS X × × × × IX × ...

  6. InnoDB之锁机制

    前两天听了姜老大关于InnoDB中锁的相关培训,刚好也在看这方面的知识,就顺便利用时间把这部分知识做个整理,方便自己理解.主要分为下面几个部分 1. InnoDB同步机制 InnoDB存储引擎有两种同 ...

  7. Innodb中怎么查看锁信息

    一.前言 上一篇说了下innodb中锁的大概意思, 这篇说说怎么查看加的哪些锁.不然后续出现死锁或者锁等待都不知道为什么. 二.底层基础表信息 在学会如何查看有哪些锁信息时, 需要了解一些基础表信息, ...

  8. Innodb中的事务隔离级别和锁的关系

    前言: 我们都知道事务的几种性质,数据库为了维护这些性质,尤其是一致性和隔离性,一般使用加锁这种方式.同时数据库又是个高并发的应用,同一时间会有大量的并发访问,如果加锁过度,会极大的降低并发处理能力. ...

  9. Innodb中的事务隔离级别和锁的关系(转)

    原文:http://tech.meituan.com/innodb-lock.html 前言: 我们都知道事务的几种性质,数据库为了维护这些性质,尤其是一致性和隔离性,一般使用加锁这种方式.同时数据库 ...

随机推荐

  1. FreeBie—免费设计师专用素材网

    FreeBie—免费设计师专用素材网 网站地址: https://freebiesupply.com/ 网站分类: 素材 浏览次数: 192 标签: 设计素材 Freebie Supply 是国外一家 ...

  2. Docker Mysql数据库主从同步配置方法

    一.背景 最近在做内部平台架构上的部署调整,顺便玩了一下数据库的主从同步,特此记录一下操作- 二.具体操作 1.先建立数据存放目录(-/test/mysql_test/) --mysql --mast ...

  3. 内核中的锁机制--RCU

    一. 引言 众所周知,为了保护共享数据,需要一些同步机制,如自旋锁(spinlock),读写锁(rwlock),它们使用起来非常简单,而且是一种很有效的同步机制,在UNIX系统和Linux系统中得到了 ...

  4. shell脚本:Kill掉MySQL中所有sleep的client线程

    分享一个shell脚本,实现kill掉mysql中所有的sleep状态的client线程,有需要的朋友,可以参考研究下. 文件名称:killsleep.sh. #It is used to kill ...

  5. 使用PIP扩展BTARN

    下载安装部署 从GS1 US RosettaNet下载相应的PIP文件  新建BizTalk解决方案并设置签名 添加->现有项(C:\Program Files (x86)\Microsoft ...

  6. 自定义命令杀死 java 进程 alias kjava

    alias kjava='ps -ef|grep ProcessName |awk "{print $2}"|xargs kill -9' 上面脚本放在杀JAVA进程中,会出现一些 ...

  7. Linux 安装Zookeeper集群

    1.解压,配置环境变量 export ZOOKEEPER_HOME=/usr/local/zkexport PATH=.:$HADOOP_HOME/bin:$ZOOKEEPER_HOME/bin:$J ...

  8. Guava Cache用法介绍<转>

    Guava Cache是在内存中缓存数据,相比较于数据库或redis存储,访问内存中的数据会更加高效.Guava官网介绍,下面的这几种情况可以考虑使用Guava Cache: 愿意消耗一些内存空间来提 ...

  9. 【javascript】九宫格抽奖组件设计

    一些主要点 1. 转圈的顺序(顺时针或者逆时针): 2. 转圈的速率(从慢到快再到慢): 3. 位置的问题(下一次抽奖的起始位置是上一次抽奖的结束位置): 4. 转圈的圈数或者移动的次数. 基本原理 ...

  10. MVC教程四:Controller向View传值的几种方式

    一.通过ViewData传值 MVC从开始版本就一直支持使用ViewData将Controller里面的数据传递到View.ViewData定义如下: 从上面的截图中可以看出,ViewData里面存的 ...