MySQL锁:03.InnoDB行锁
通过索引实现行锁,在索引记录上加锁。
没有索引就无法实现行锁,升级成全表记录锁,等同于表锁。
理解InnoDB独特的行锁运行机制,认识特有的四种行锁粒度——lock_ordinary、lock_gap、lock_rec_not_gap、lock_insert_intention

InnoDB 行锁
- 默认都是加lock_ordinary锁
 - 如果是唯一索引列上的等值查询,则退化成lock_rec_not_gap
 - 所有版本,非唯一索引列上的范围查询,遇到第一个不符合条件的记录也会加上lock_ordinary。
 - 8.0.18版本以前,主要指<=场景:唯一索引列上的范围查询,遇到第一个不符合条件的记录也会加上lock_ordinary ,在RC下会释放,RR下不会释放。
 - 8.0.18版本以前,非唯一索引列上的等值查询,向右遍历遇到第一个不符合条件的记录时,先加上lock_ordinary,再退化成lock_gap。
 
锁排查可以用的视图和数据字典
mysql> show engine innodb status \G
mysql> select * from performance_schema.data_lock_waits;
mysql> select * from performance_schema.data_locks;
mysql> select * from performance_schema.metadata_locks;
InnoDB 行锁兼容性
| 请求的锁类型 | 请求的锁类型 | 请求的锁类型 | 请求的锁类型 | ||
|---|---|---|---|---|---|
| lock_ordinary | lock_rec_not_gap | lock_gap | lock_insert_intention | ||
| 已获得的锁类型 | lock_ordinary | X | X | O | X | 
| 已获得的锁类型 | lock_rec_not_gap | X | X | O | O | 
| 已获得的锁类型 | lock_gap | O | O | O | X | 
| 已获得的锁类型 | lock_insert_intention | O | O | O | O | 
gap只和insert intention锁冲突
insert intention和任何锁都不冲突,除非也在相同位置做意向插入锁
先获得意向插入锁的,再尝试上gap lock是可以的
但是反过来 ,先获得gap lock的,再尝试加上意向插入锁便会阻塞,
原因是:先获得意向插入锁时,实际上插入已经成功,意向插入锁会被转变为对具体记录的ordinary 或 rec_not_gap ,此时二者都与lock gap兼容。
InnoDB行锁之共享锁
共享锁:
- 不允许其他事务修改被锁定的行,只能读
 - select .. for share/ lock in share mode
 - 自动提交模式下的普通select是一致性非锁定读,不加锁。
 
自动提交模式下, 不使用begin开启事务,直接select的话:
select * from xxx where .. 不加锁
select * from xxx where .. for share ,也查询不到加锁, 但是实际上是加锁的,只不过锁的时间及其的短暂。
验证:

此时,用排他锁来验证自动提交模式的for share究竟是否产生锁动作。

可以看出,自动提交模式下select(不加for share)是一致性非锁定读,但是加for share后,是会有锁定动作的,只不过没有阻塞的情况下,锁的持续时间是非常短暂的。
查看InnoDB锁
开启参数:innodb_status_output_locks=1; 以支持使用 show engine innodb status 查看锁详情。
mysql> begin ; select * from k1 where id=4 for update;
Query OK, 0 rows affected (0.00 sec) mysql> show engine innodb status \G
..
MySQL thread id 31, OS thread handle 139620328457984, query id 1297 localhost root
**TABLE LOCK table `kk`.`k1` trx id 1901 lock mode IX**
RECORD LOCKS space id 3 page no 10 n bits 1056 index id of table `kk`.`k1` trx id 1901 lock_mode X
Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 80000004; asc ;;
1: len 6; hex 000000000902; asc ;; RECORD LOCKS space id 3 page no 9 n bits 320 index GEN_CLUST_INDEX of table `kk`.`k1` trx id 1901 lock_mode X locks rec but not gap
Record lock, heap no 248 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
0: len 6; hex 000000000902; asc ;;
1: len 6; hex 000000000663; asc c;;
2: len 7; hex 82000000940110; asc ;;
3: len 4; hex 80000004; asc ;;
4: SQL NULL;
5: SQL NULL; RECORD LOCKS space id 3 page no 10 n bits 1056 index id of table `kk`.`k1` trx id 1901 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 80000016; asc ;;
1: len 6; hex 000000000200; asc ;;
.. - space id 3 表的表空间ID
- page no 10 锁所在datapage的ID
- heap no 4,slot no,记录在page物理上的第几个位置。
也可以查询P_S表
mysql> select * from performance_schema.data_locks;
+--------+----------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+---------------+-------------+-----------+
| ENGINE | ENGINE_LOCK_ID | ENGINE_TRANSACTION_ID | THREAD_ID | EVENT_ID | OBJECT_SCHEMA | OBJECT_NAME | PARTITION_NAME | SUBPARTITION_NAME | INDEX_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA |
+--------+----------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+---------------+-------------+-----------+
| INNODB | 139620969519720:1072:139620864029240 | 2211 | 85 | 79 | kk | k1 | NULL | NULL | NULL | 139620864029240 | TABLE | IX | GRANTED | NULL |
| INNODB | 139620969519720:15:4:5:139620864026200 | 2211 | 85 | 79 | kk | k1 | NULL | NULL | PRIMARY | 139620864026200 | RECORD | X,REC_NOT_GAP | GRANTED | 4 |
+--------+----------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+---------------+-------------+-----------+
2 rows in set (0.00 sec)
再看下IS锁的情况
mysql> begin ; select * from k1 where id=4 for share;
Query OK, 0 rows affected (0.00 sec)
mysql> show engine innodb status \G
看不到IS锁信息。
1 row in set (0.00 sec)
mysql> select * from performance_schema.data_locks;
+--------+----------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+---------------+-------------+-----------+
| ENGINE | ENGINE_LOCK_ID             | ENGINE_TRANSACTION_ID | THREAD_ID | EVENT_ID | OBJECT_SCHEMA | OBJECT_NAME | PARTITION_NAME | SUBPARTITION_NAME | INDEX_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_MODE   | LOCK_STATUS | LOCK_DATA |
+--------+----------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+---------------+-------------+-----------+
| INNODB | 139620969519720:1072:139620864029240  |    421095946230376 |    85 |    83 | kk      | k1     | NULL      | NULL       | NULL    |    139620864029240 | TABLE   | IS      | GRANTED   | NULL   |
| INNODB | 139620969519720:15:4:5:139620864026200 |    421095946230376 |    85 |    83 | kk      | k1     | NULL      | NULL       | PRIMARY  |    139620864026200 | RECORD  | S,REC_NOT_GAP | GRANTED   | 4     |
+--------+----------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+---------------+-------------+-----------+
2 rows in set (0.00 sec)
验证一下IS和IX的兼容
t1:
mysql> begin ; select * from k1 where id=4 for share;
Query OK, 0 rows affected (0.00 sec)
+------+------+------+
| id  | dtl | name |
+------+------+------+
|  4 | NULL | NULL |
+------+------+------+
1 row in set (0.00 sec)
t2:
mysql> begin ; select * from k1 where id=11 for update;
Query OK, 0 rows affected (0.00 sec)
Empty set (0.00 sec)
--注意:加锁加不在相同行,否则hang。
t3:
mysql> select * from performance_schema.data_locks;
+--------+----------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+---------------+-------------+------------------------+
| ENGINE | ENGINE_LOCK_ID             | ENGINE_TRANSACTION_ID | THREAD_ID | EVENT_ID | OBJECT_SCHEMA | OBJECT_NAME | PARTITION_NAME | SUBPARTITION_NAME | INDEX_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_MODE   | LOCK_STATUS | LOCK_DATA       |
+--------+----------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+---------------+-------------+------------------------+
| INNODB | 139620969519720:1072:139620864029240  |         2212 |    85 |    89 | kk      | k1     | NULL      | NULL       | NULL    |    139620864029240 | TABLE   | IX      | GRANTED   | NULL          |
| INNODB | 139620969519720:15:4:1:139620864026200 |         2212 |    85 |    89 | kk      | k1     | NULL      | NULL       | PRIMARY  |    139620864026200 | RECORD  | X       | GRANTED   | supremum pseudo-record |
| INNODB | 139620969521464:1072:139620864041176  |    421095946232120 |    84 |   110 | kk      | k1     | NULL      | NULL       | NULL    |    139620864041176 | TABLE   | IS      | GRANTED   | NULL          |
| INNODB | 139620969521464:15:4:5:139620864038296 |    421095946232120 |    84 |   110 | kk      | k1     | NULL      | NULL       | PRIMARY  |    139620864038296 | RECORD  | S,REC_NOT_GAP | GRANTED   | 4           |
+--------+----------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+---------------+-------------+------------------------+
4 rows in set (0.00 sec)
InnoDB行锁实现机制
- 基于索引实现,逐行检查,逐行加锁
 - 没有索引的列上需要加锁时,会先对所有记录加锁,再根据实际情况决定是否释放锁。
 - 辅助索引上加锁时,同时要回溯到主键索引上再加一次锁。
 - 加锁的基本单位默认时lock_ordinary,当索引就具有唯一性的时候退化为lock_rec_not_gap
 - 等值条件逐行加锁时,会向右遍历到第一个不满足条件的记录,然后lock_ordinary退化为lock_gap
 - 如果发生唯一性检测(insert\update动作),那么会发生lock_ordinary , 再退化成lock_rec_not_gap
 - 唯一索引的范围条件加锁时,也会对第一个不满足条件的记录加锁
 
对普通索引上锁
普通索引next-key lock + 主键 not gap + 普通索引的下一个记录的gap lock(见示意图)。
mysql> select * from k2;
+----+------+------+
| id | dtl  | un   |
+----+------+------+
| 1  |  1   |  1   |
| 2  |  2   |  2   |
| 3  |  5   |  5   |  (回溯到pk上锁)
------------------------------------------  <- gap (同时锁住普通索引下一个记录前的gap)
| 4  |  7   |  7   |*
| 5  |  11  |  11  |
+----+------+------+
5 rows in set (0.00 sec)
- 辅助索引上锁的验证实验
 
mysql> show create table k2\G
*************************** 1. row ***************************
    Table: k2
Create Table: CREATE TABLE `k2` (
 `id` int NOT NULL AUTO_INCREMENT,
 `dtl` int DEFAULT NULL,
 `un` int DEFAULT NULL,
 PRIMARY KEY (`id`),
 UNIQUE KEY `dtl` (`dtl`),
 KEY `un` (`un`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)
mysql> select * from k2;
+----+------+------+
| id | dtl | un  |
+----+------+------+
| 1 |  1 |  1 |
| 2 |  2 |  2 |
| 3 |  5 |  5 |
|  4 |  7 |  7 |*
| 5 |  11 |  11 |
+----+------+------+
5 rows in set (0.00 sec)
mysql> begin ; select * from k2 where un=5 for update;
Query OK, 0 rows affected (0.00 sec)
+----+------+------+
| id | dtl | un  |
+----+------+------+
| 3 |  5 |  5 |
+----+------+------+
1 row in set (0.00 sec)
mysql> select * from performance_schema.data_locks\G
*************************** 1. row ***************************
        ENGINE: INNODB
    ENGINE_LOCK_ID: 139620969521464:1061:139620864041176
ENGINE_TRANSACTION_ID: 1944
      THREAD_ID: 70
       EVENT_ID: 65
    OBJECT_SCHEMA: kk
     OBJECT_NAME: k2
    PARTITION_NAME: NULL
  SUBPARTITION_NAME: NULL
      INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 139620864041176
       LOCK_TYPE: TABLE*
      LOCK_MODE: IX*
     LOCK_STATUS: GRANTED
      LOCK_DATA: NULL
*************************** 2. row ***************************
        ENGINE: INNODB
    ENGINE_LOCK_ID: 139620969521464:4:6:9:139620864038296
ENGINE_TRANSACTION_ID: 1944
      THREAD_ID: 70
       EVENT_ID: 65
    OBJECT_SCHEMA: kk
     OBJECT_NAME: k2
    PARTITION_NAME: NULL
  SUBPARTITION_NAME: NULL
      INDEX_NAME: un*
OBJECT_INSTANCE_BEGIN: 139620864038296
      LOCK_TYPE: RECORD (nextkey-lock)*
      LOCK_MODE: X
     LOCK_STATUS: GRANTED
       LOCK_DATA: 5, 3    (un key value, primary key value, index Condion特性)*
*************************** 3. row ***************************
        ENGINE: INNODB
    ENGINE_LOCK_ID: 139620969521464:4:4:11:139620864038640
ENGINE_TRANSACTION_ID: 1944
      THREAD_ID: 70
       EVENT_ID: 65
    OBJECT_SCHEMA: kk
     OBJECT_NAME: k2
    PARTITION_NAME: NULL
  SUBPARTITION_NAME: NULL
      INDEX_NAME: PRIMARY*
OBJECT_INSTANCE_BEGIN: 139620864038640
      LOCK_TYPE: RECORD
       LOCK_MODE: X,REC_NOT_GAP*
     LOCK_STATUS: GRANTED
      LOCK_DATA: 3*
*************************** 4. row ***************************
        ENGINE: INNODB
    ENGINE_LOCK_ID: 139620969521464:4:6:10:139620864038984
ENGINE_TRANSACTION_ID: 1944
      THREAD_ID: 70
       EVENT_ID: 65
    OBJECT_SCHEMA: kk
     OBJECT_NAME: k2
    PARTITION_NAME: NULL
  SUBPARTITION_NAME: NULL
      INDEX_NAME: un*
OBJECT_INSTANCE_BEGIN: 139620864038984
      LOCK_TYPE: RECORD
      LOCK_MODE: X,GAP*      确认下一个记录不符合条件,回退nextkey-lock 为 lock-gap.
     LOCK_STATUS: GRANTED
      LOCK_DATA: 7, 4   (un key value, primary key value, index Condion特性)*
4 rows in set (0.00 sec)
InnoDB隐式、显式锁
显式锁(explicit-lock)
- select .. from .. where .. for update / for share
 
隐式锁(implicit-lock)
- update set .. where ..
 - 任何辅助索引上锁,或非索引列上锁,都要回溯到主键上再加锁。
 - 和其他session有冲突时,隐式锁转换为显式锁。(没实验验证出来。)
 
如果发生唯一性检测(insert\update动作),那么会发生lock_ordinary , 再退化成lock_rec_not_gap
MySQL锁:03.InnoDB行锁的更多相关文章
- Mysql研磨之InnoDB行锁模式
		
事务并发带来的一些问题 (1)更新丢失(LostUpdate):当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题最后的更新覆盖了由其 ...
 - mysql 开发进阶篇系列 9 锁问题 (Innodb 行锁实现方式)
		
一.概述 Innodb 行锁是通过给索引上的索引项加锁来实现的.这一点与(oracle,sql server)不同后者是通过在数据块中对相应的数据行加锁.这意味着只有通过索引条件检索数据,innodb ...
 - MySQL数据库锁机制之MyISAM引擎表锁和InnoDB行锁详解
		
转 http://blog.csdn.net/hsd2012/article/details/51112009 转 http://blog.csdn.net/e421083458/article/de ...
 - Innodb行锁源码学习(一)
		
Innodb是mysql数据库中目前最流行的存储引擎,innodb相对其它存储引擎一个很大的特点是支持事务,并且支持行粒度的锁.今天我重点跟大家分享下innodb行锁实现的基础知识.由于篇幅比较大,文 ...
 - Mysql InnoDB行锁实现方式(转)
		
Mysql InnoDB行锁实现方式 InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点MySQL与Oracle不同,后者是通过在数据块中对相应数据行加锁来实现的.InnoDB这种行锁实现特点 ...
 - Mysql InnoDB行锁实现方式
		
Mysql InnoDB行锁实现方式 InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点MySQL与Oracle不同,后者是通过在数据块中对相应数据行加锁来实现的.InnoDB这种行锁实现特点 ...
 - MySQL锁---InnoDB行锁需要注意的细节
		
前言 换了工作之后,接近半年没有发博客了(一直加班),emmmm.....今天好不容易有时间,记录下工作中遇到的一些问题,接下来应该重拾知识点了.因为新公司工作中MySQL库经常出现查询慢,锁等待,节 ...
 - mysql--->innodb引擎什么时候表锁什么时候行锁?
		
mysql innodb引擎什么时候表锁什么时候行锁? InnoDB基于索引的行锁 InnoDB行锁是通过索引上的索引项来实现的,这一点MySQL与Oracle不同,后者是通过在数据中对相应数据行加锁 ...
 - Mysql  MyISAM与InnoDB 表锁行锁以及分库分表优化
		
一. 两种存储引擎:MyISAM与InnoDB 区别与作用 1. count运算上的区别: 因为MyISAM缓存有表meta-data(行数等),因此在做COUNT(*)时对于一个结构很好的查询是不需 ...
 
随机推荐
- Boost命令行解释器的简单使用:Boost.Program_options
			
简介 如果使用比较多的命令行程序的话,对于命令行参数的输入肯定不会陌生,大部分的程序都是通过类似下面的形式进行输入的,比如熟悉的ls ls --all -l --color=auto 这里面包含了三种 ...
 - git 最新笔记,工作中的必会技能
			
1.状态查看: git status 可以查看工作区,暂存区的状态 untracked 在暂存区没有该文件 modified 修改过 staged 使用git add 暂存过 2.添加操作: git ...
 - Android 滑动删除控件推荐
			
implementation 'com.github.mcxtzhang:SwipeDelMenuLayout:V1.3.0' <?xml version="1.0" enc ...
 - Fastjson远程代码执行漏洞复现
			
fastjson漏洞简介 Fastjson是一个Java库,可用于将Java对象转换为其JSON表示形式.它还可以用于将JSON字符串转换为等效的Java对象,fastjson爆出多个反序列化远程命令 ...
 - cProfile分析程序性能
			
Python标准库中提供了三种用来分析程序性能的模块,分别是cProfile, profile和hotshot,另外还有一个辅助模块stats.这些模块提供了对Python程序的确定性分析功能,同时也 ...
 - Vegas技巧分享,如何实现5.1立体声道
			
Vegas Pro 15除了有各种好用的功能之外,还可以使用此软件制作5.1立体声道的音乐.如果你家里有多个音箱,那么你再也不用担心找不到5.1声道的音乐了,Vegas完全可以帮你实现. 打开简体中文 ...
 - python应用(2):写个python程序给自己用
			
用python写一个程序,然后在命令行上执行,看不到界面(UI),这种程序很常见了,叫命令行程序.然而很多人,特别是不懂程序的人,更需要看到的是一个有界面的,能通过鼠标操作的程序,毕竟已经迈进&quo ...
 - 简化的鸿蒙WiFi接口,仅需几行代码,简单易用!
			
使用鸿蒙原始WiFI API接口进行编程,整个过程稍显繁琐,为此我们对鸿蒙原始WiFi API接口做了一层封装,形成了一套更简单易用的接口. 简化后的API接口 STA模式 // 连接WiFi热点,并 ...
 - 容器中实现拉取其它服务器的jar包程序
			
缘由:在做接口自动化测试时,若业务场景有一个前置仓库,在该仓库内完成一系列的场景测试,一旦某一场景测试失败,脏数据对环境造成影响则需要清理: 1.我容器的内核系统为Debian GNU/Linux 1 ...
 - 创建topic
			
sh kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic ...