MySQL · 特性分析 · innodb 锁分裂继承与迁移
http://mysql.taobao.org/monthly/2016/06/01/
innodb行锁简介
- 行锁类型
LOCK_S:共享锁
LOCK_X: 排他锁
- GAP类型
LOCK_GAP:只锁间隙
LOCK_REC_NO_GAP:只锁记录
LOCK_ORDINARY: 锁记录和记录之前的间隙
LOCK_INSERT_INTENTION: 插入意向锁,用于insert时检查锁冲突
每个行锁由锁类型和GAP类型组成
例如:
LOCK_X|LOCK_ORDINARY 表示对记录和记录之前的间隙加排他锁
LOCK_S|LOCK_GAP 表示只对记录前的间隙加共享锁
锁的兼容性:
值得注意的是,持有GAP的锁(LOCK_GAP和LOCK_ORDINARY)与其他非LOCK_INSERT_INTENTION的锁都是兼容的,也就是说,GAP锁就是为了防止插入的。
详细可以参考之前的月报
innodb 锁分裂、继承与迁移
这里的锁分裂和合并,只是针对innodb行锁而言的,而且一般只作用于GAP类型的锁。
锁分裂
插入的记录的间隙存在GAP锁,此时此GAP需分裂为两个GAP
lock_rec_inherit_to_gap_if_gap_lock:
for (lock = lock_rec_get_first(block, heap_no);
lock != NULL;
lock = lock_rec_get_next(heap_no, lock)) {
if (!lock_rec_get_insert_intention(lock)
&& (heap_no == PAGE_HEAP_NO_SUPREMUM
|| !lock_rec_get_rec_not_gap(lock))) {
lock_rec_add_to_queue(
LOCK_REC | LOCK_GAP | lock_get_mode(lock),
block, heir_heap_no, lock->index,
lock->trx, FALSE);
}
}
锁继承
删除的记录前存在GAP锁,此GAP锁会继承到要删除记录的下一条记录上
lock_rec_inherit_to_gap:
for (lock = lock_rec_get_first(block, heap_no);
lock != NULL;
lock = lock_rec_get_next(heap_no, lock)) {
if (!lock_rec_get_insert_intention(lock)
&& !((srv_locks_unsafe_for_binlog
|| lock->trx->isolation_level
<= TRX_ISO_READ_COMMITTED)
&& lock_get_mode(lock) ==
(lock->trx->duplicates ? LOCK_S : LOCK_X))) {
lock_rec_add_to_queue(
LOCK_REC | LOCK_GAP | lock_get_mode(lock),
heir_block, heir_heap_no, lock->index,
lock->trx, FALSE);
}
}
锁迁移
B数结构变化,锁信息也会随之迁移. 锁迁移过程中也涉及锁继承。
锁分裂示例
- 锁分裂例子
set global tx_isolation='repeatable-read';
create table t1(c1 int primary key, c2 int unique) engine=innodb;
insert into t1 values(1,1);
begin;
# supremum 记录上加 LOCK_X|LOCK_GAP 锁住(1~)
select * from t1 where c2=2 for update;
# 发现插入(3,3)的间隙存在GAP锁,因此给(3,3)加LOCK_X | LOCK_GAP锁。这样依然锁住了(1~)
insert into t1 values(3,3);
这里如果插入(3,3)没有给(3,3)加LOCK_X | LOCK_GAP,那么其他连接插入(2,2)就可以成功
锁继承示例
隔离级别repeatable-read

验证:session 1执行insert into t1 values(1,1)发生了锁等待,说明(2,2)上有gap锁
mysql> select * from information_schema.innodb_locks;
+------------------------+-------------+-----------+-----------+-----------------+------------+------------+-----------+----------+-----------+
| lock_id | lock_trx_id | lock_mode | lock_type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data |
+------------------------+-------------+-----------+-----------+-----------------+------------+------------+-----------+----------+-----------+
| 16582717714:888654:4:3 | 16582717714 | X,GAP | RECORD | `cleaneye`.`t1` | c2 | 888654 | 4 | 3 | 2 |
| 16582692183:888654:4:3 | 16582692183 | X,GAP | RECORD | `cleaneye`.`t1` | c2 | 888654 | 4 | 3 | 2 |
+------------------------+-------------+-----------+-----------+-----------------+------------+------------+-----------+----------+-----------+
2 rows in set (0.01 sec)
其中session 2 在(2,2) 加了LOCK_X|LOCK_GAP
session 1 在(2,2) 加了LOCK_X|LOCK_GAP|LOCK_INSERT_INTENTION. LOCK_INSERT_INTENTION与LOCK_GAP冲突发生等待
- 隔离级别read-committed

验证: session 1执行insert into t1 values(1)发生了锁等待,说明(2)上有gap锁
mysql> select * from information_schema.innodb_locks;
+------------------------+-----------------+-----------+-----------+-------------+------------+------------+-----------+----------+-----------+
| lock_id | lock_trx_id | lock_mode | lock_type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data |
+------------------------+-----------------+-----------+-----------+-------------+------------+------------+-----------+----------+-----------+
| 1705:32:3:3 | 1705 | X,GAP | RECORD | `test`.`t1` | PRIMARY | 32 | 3 | 3 | 2 |
| 421590768578232:32:3:3 | 421590768578232 | S,GAP | RECORD | `test`.`t1` | PRIMARY | 32 | 3 | 3 | 2 |
+------------------------+-----------------+-----------+-----------+-------------+------------+------------+-----------+----------+-----------+
X.GAP insert 加锁LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION
S.GAP 加锁LOCK_S|LOCK_GAP,记录(2)从删除的记录(1)继承过来的GAP锁
而实际在读提交隔离级别上,insert into t1 values(1)应该可以插入成功,不需要等待的,这个锁是否继承值得商榷。
来看一个插入成功的例子

隔离级别serializable

验证方法同read-committed。
B树结构变化与锁迁移
B树节点发生分裂,合并,删除都会引发锁的变化。锁迁移的原则是,B数结构变化前后,锁住的范围保证不变。
我们通过例子来说明
节点分裂
假设原节点A(infimum,1,3,supremum) 向右分裂为B(infimum,1,supremum), C(infimum,3,supremum)两个节点
> infimum为节点中虚拟的最小记录,supremum为节点中虚拟的最大记录假设原节点A上锁为3上LOCK_S|LOCK_ORIDNARY,supremum为LOCK_S|LOCK_GAP,实际锁住了(1~)
锁迁移过程大致为:- 将3上的gap锁迁移到C节点3上
- 将A上supremum迁移继承到C的supremum上
- 将C上最小记录3的锁迁移继承到B的supremum上
迁移完成后锁的情况如下(lock_update_split_right)
B节点:suprmum LOCK_S|LOCK_GAP
C节点:3 LOCK_S|LOCK_ORINARY, suprmum LOCK_S|GAP迁移后仍然锁住了范围(1~)
节点向左分裂情形类似
节点合并
以上述节点分裂的逆操作来讲述合并过程
B(infimum,1,supremum), C(infimum,3,supremum)两个节点,向左合并为A节点(infimum,1,3,supremum)
其中B,C节点锁情况如下
B节点:suprmum LOCK_S|LOCK_GAP
C节点:3 LOCK_S|LOCK_ORINARY, suprmum LOCK_S|GAP迁移流程如下(lock_update_merge_left):
1)将C节点锁记录3迁移到B节点
2)将B节点supremum迁移继承到A的supremum上
迁移后仍然锁住了范围(1~)
节点向右合并情形类似
节点删除
如果删除节点存在左节点,则将删除节点符合条件的锁,迁移继承到左节点supremum上
否则将删除节点符合条件的锁,迁移继承到右节点最小用户记录上
参考lock_update_discard
锁继承相关的BUG
bug#73170 二级唯一索引失效。这个bug触发条件是删除的记录没有被purge, 锁还没有被继承的。如果锁继承了就不会出现问题。
bug#76927 同样是二级唯一索引失效。这个bug是锁继承机制出了问题。
以上两个bug详情参考这里
MySQL · 特性分析 · innodb 锁分裂继承与迁移的更多相关文章
- innodb 锁分裂继承与迁移
innodb行锁简介 行锁类型 LOCK_S:共享锁 LOCK_X: 排他锁 GAP类型 LOCK_GAP:只锁间隙 LOCK_REC_NO_GAP:只锁记录 LOCK_ORDINARY: 锁记录和记 ...
- MySQL · 特性分析 · 优化器 MRR & BKA【转】
MySQL · 特性分析 · 优化器 MRR & BKA 上一篇文章咱们对 ICP 进行了一次全面的分析,本篇文章小编继续为大家分析优化器的另外两个选项: MRR & batched_ ...
- MySQL · 特性分析 · MDL 实现分析
http://mysql.taobao.org/monthly/2015/11/04/ 前言 在MySQL中,DDL是不属于事务范畴的,如果事务和DDL并行执行,操作相关联的表的话,会出现各种意想不到 ...
- MySQL · 特性分析 · 内部临时表
http://mysql.taobao.org/monthly/2016/06/07/#rd MySQL中的两种临时表 外部临时表 通过CREATE TEMPORARY TABLE 创建的临时表,这种 ...
- MySQL 5.7 InnoDB锁
简介 参考https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html#innodb-gap-locks. InnoDB引擎实现了标准的行级别 ...
- Innodb锁的类型
Innodb锁的类型 行锁(record lock) 行锁总是对索引上锁,如果某个表没有定义索引,mysql就会使用默认创建的聚集索引,行锁有S锁和X锁两种类型. 共享锁和排它锁 Innodb锁有两种 ...
- mysql InnoDB锁等待的查看及分析
说明:前面已经了解了InnoDB关于在出现锁等待的时候,会根据参数innodb_lock_wait_timeout的配置,判断是否需要进行timeout的操作,本文档介绍在出现锁等待时候的查看及分析处 ...
- MySQL数据恢复和复制对InnoDB锁机制的影响
MySQL通过BINLOG记录执行成功的INSERT,UPDATE,DELETE等DML语句.并由此实现数据库的恢复(point-in-time)和复制(其原理与恢复类似,通过复制和执行二进制日志使一 ...
- InnoDB锁机制分析
InnoDB锁机制常常困扰大家,不同的条件下往往表现出不同的锁竞争,在实际工作中经常要分析各种锁超时.死锁的问题.本文通过不同条件下的实验,利用InnoDB系统给出的各种信息,分析了锁的工作机制.通过 ...
随机推荐
- Team them up!
题意: 给出n个人以及认识其他人的情况,现在要把所有人分成两队,每队至少一人,求使两队人数差距最小且每队内部的人都相互认识的分队情况. 分析: 这道题让我学习到了不少,首先看到使差距最小就想到了背包, ...
- HDU5715 XOR 游戏 二分+字典树+dp
当时Astar复赛的时候只做出1题,赛后补题(很长时间后才补,懒真是要命),发现这是第二简单的 分析: 这个题,可以每次二分区间的最小异或和 进行check的时候用dp进行判断,dp[i][j]代表前 ...
- XTUOJ1247 Pair-Pair 预处理+暴力
分析:开个1000*1000的数组,预处理矩阵和,然后分类讨论就好 时间复杂度:O(n) #include <cstdio> #include <iostream> #incl ...
- linux的文件属性介绍、目录及路径表示方法
一.认识linux文件 认识linux下的文件需要先学习命令:ls. 该命令用于显示指定目录下的内容,其中最常用的参数有: -l显示目录和文件的完整属性信息 -a显示所有文件和目录,包含隐藏文件和目录 ...
- OpenGL超级宝典第5版&&缓冲区
缓冲区有很多用途:可以保存顶点数据,像素数据,纹理数据,着色器处理的输入,不同着色器阶段的输出. 缓冲区保存在GPU内存中,提供高速有效的访问. 像素缓冲区对象: GLuint pixBuffer ...
- STM32查看系统时钟
调用库函数RCC_GetClocksFreq,该函数可以返回片上的各种时钟的频率 函数原形 void RCC_GetClocksFreq(RCC_ClocksTypeDef* RCC_Clock ...
- 使用 svn://ip/filename 方式访问svn 资源库
a. 下载 SVN的官方网站为 http://subversion.tigris.org/,当前最新版本为1.4.2.Windows下的二进制安装包分为两种,一种是以setup结尾的安装文件,另一种是 ...
- c++ 概念及学习/c++ concept&learning(一)
学习过计算机组成原理就会知道,处理器会从主存中取得指令,然后进行解释执行.而他们的交流方式是以二进制方式进行的,也就是他们只能识别1和0 :其实计算机是不知道1和0的,现在的实现方式是以高电压与低电压 ...
- Language Basics:语言基础
Java包含多种变量类型:Instance Variables (Non-Static Fields)(实例变量):是每个对象特有的,可以用来区分各个实例Class Variables (Static ...
- Spark系列(五)Master主备切换机制
Spark Master主备切换主要有两种机制,之中是基于文件系统,一种是基于Zookeeper.基于文件系统的主备切换机制需要在Active Master挂掉后手动切换到Standby Master ...