http://mysql.taobao.org/monthly/2016/06/01/

innodb行锁简介

  1. 行锁类型
    LOCK_S:共享锁
LOCK_X: 排他锁
  1. 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~)
    锁迁移过程大致为:

    1. 将3上的gap锁迁移到C节点3上
    2. 将A上supremum迁移继承到C的supremum上
    3. 将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 锁分裂继承与迁移的更多相关文章

  1. innodb 锁分裂继承与迁移

    innodb行锁简介 行锁类型 LOCK_S:共享锁 LOCK_X: 排他锁 GAP类型 LOCK_GAP:只锁间隙 LOCK_REC_NO_GAP:只锁记录 LOCK_ORDINARY: 锁记录和记 ...

  2. MySQL · 特性分析 · 优化器 MRR & BKA【转】

    MySQL · 特性分析 · 优化器 MRR & BKA 上一篇文章咱们对 ICP 进行了一次全面的分析,本篇文章小编继续为大家分析优化器的另外两个选项: MRR & batched_ ...

  3. MySQL · 特性分析 · MDL 实现分析

    http://mysql.taobao.org/monthly/2015/11/04/ 前言 在MySQL中,DDL是不属于事务范畴的,如果事务和DDL并行执行,操作相关联的表的话,会出现各种意想不到 ...

  4. MySQL · 特性分析 · 内部临时表

    http://mysql.taobao.org/monthly/2016/06/07/#rd MySQL中的两种临时表 外部临时表 通过CREATE TEMPORARY TABLE 创建的临时表,这种 ...

  5. MySQL 5.7 InnoDB锁

    简介 参考https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html#innodb-gap-locks. InnoDB引擎实现了标准的行级别 ...

  6. Innodb锁的类型

    Innodb锁的类型 行锁(record lock) 行锁总是对索引上锁,如果某个表没有定义索引,mysql就会使用默认创建的聚集索引,行锁有S锁和X锁两种类型. 共享锁和排它锁 Innodb锁有两种 ...

  7. mysql InnoDB锁等待的查看及分析

    说明:前面已经了解了InnoDB关于在出现锁等待的时候,会根据参数innodb_lock_wait_timeout的配置,判断是否需要进行timeout的操作,本文档介绍在出现锁等待时候的查看及分析处 ...

  8. MySQL数据恢复和复制对InnoDB锁机制的影响

    MySQL通过BINLOG记录执行成功的INSERT,UPDATE,DELETE等DML语句.并由此实现数据库的恢复(point-in-time)和复制(其原理与恢复类似,通过复制和执行二进制日志使一 ...

  9. InnoDB锁机制分析

    InnoDB锁机制常常困扰大家,不同的条件下往往表现出不同的锁竞争,在实际工作中经常要分析各种锁超时.死锁的问题.本文通过不同条件下的实验,利用InnoDB系统给出的各种信息,分析了锁的工作机制.通过 ...

随机推荐

  1. linux命令——磁盘管理cd

    Linux cd 命令可以说是Linux中最基本的命令语句,其他的命令语句要进行操作,都是建立在使用 cd 命令上的. cd指令可让用户在不同的目录间切换,但该用户必须拥有足够的权限进入目的目录. 1 ...

  2. D3D11_USAGE使用

    MSDN文档链接:http://msdn.microsoft.com/en-us/library/windows/desktop/ff476259(v=vs.85).aspx 不得不同吐槽一点的是,你 ...

  3. ORA-12162: TNS:net service name is incorrectly specified

    今天在进行修改oracle_sid环境变量的时候,将相关的环境变量值去掉,从而不能进入sqlplus,报错如下: [oracle@kel ~]$ sqlplus / as sysdba SQL*Plu ...

  4. bzoj 3143 [Hnoi2013]游走(贪心,高斯消元,期望方程)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3143 [题意] 给定一个无向图,从1走到n,走过一条边得到的分数为边的标号,问一个边的 ...

  5. [Hive - LanguageManual] Hive Concurrency Model (待)

    Hive Concurrency Model Hive Concurrency Model Use Cases Turn Off Concurrency Debugging Configuration ...

  6. Codeforces Educational Codeforces Round 15 A. Maximum Increase

    A. Maximum Increase time limit per test 1 second memory limit per test 256 megabytes input standard ...

  7. Another mysql daemon already running with the same unix socket

    在国外网站发现的解决方法. 原因多个Mysql进程使用了同一个socket. 两个方法解决: 第一个是立即关机 使用命令 shutdown -h now 关机,关机后在启动,进程就停止了. 第二个直接 ...

  8. Linux Oracle服务启动&停止脚本与开机自启动

    在CentOS 6.3下安装完Oracle 10g R2,重开机之后,你会发现Oracle没有自行启动,这是正常的,因为在Linux下安装Oracle的确不会自行启动,必须要自行设定相关参数,首先先介 ...

  9. 教程-在F9后提示内存错误,点击了乎略,之后怎么取消乎略?

    问题现象:F9后,调试程序,提示内存错误,点击了“乎略”.之后再也没有出现错误了.可是想改这个BUG时,没法取消乎略了. 问题原因:在DLEPHI的选项中是这么一个地方是可以设置的. 问题处理:打开D ...

  10. 贪心-poj-2437-Muddy roads

    题目链接: http://poj.org/problem?id=2437 题目意思: 给n个区间,每次可以用长度为L的棒A去覆盖,求将所有区间覆盖至少需要几次.给的区间不覆盖. 解题思路: 简单贪心. ...