首页 > 其他分享 >InnoDB 的锁和事务模型

InnoDB 的锁和事务模型

时间:2023-08-18 18:22:59浏览次数:373  
标签:插入 事务 间隙 lock 模型 记录 索引 InnoDB

目录

InnoDB 的锁

InnoDB 使用的锁类型包括:

  • 共享锁(shared lock)和排它锁(exclusive lock)

  • 意向锁(intention lock)

  • 记录锁(record lock)

  • 间隙锁(gap lock)

  • 下一钥匙锁(next-key lock)

  • 插入意向锁(insert-intention lock)

  • AUTO-INC 锁(AUTO-INC lock)

  • 空间索引的断言锁(predicate Locks for spatial indexes)

共享锁和独占锁

InnoDB 实现了标准的行级锁定,其中,有两种类型的锁:

如果事务 T1 在行 r 上持有共享锁,则另一个不同事务 T2 对行 r 的锁的请求将按如下方式处理:

  • T2 请求获取共享锁,T2 能立即持有共享锁,结果,T1 和 T2都持有对行的 r 的共享锁;

  • T2 请求获取排它锁,T2 不能立即持有共享锁。

如果事务 T1 持有行 r 上的排它锁,则另一个不同事务 T2 对行 r 上的任一类型的锁的请求,都不能立即被授予。相反,其他事务必须等待事务 T1 释放在行 r 上的锁定。

意向锁

InnoDB 支持多粒度锁定(multiple granularity locking),允许行锁和表锁共存

例如,在指定的表上执行 LOCK TABLES ... WRITE 语句,将会持有排它锁。

InnoDB 使用了意向锁(intention locks) 实现了多粒度级别的锁定,意向锁是表级锁,表明事务稍后需要对表中的行使用哪种类型的锁(共享锁或排它锁),意向锁有两种类型:

  • 意向共享锁(intention shared lock,IS):表示事务打算在表中的各个行上设置共享锁;

    例如,SELECT ... FOR SHARE 会设置 IS 锁。

  • 意向排它锁(intention exclusive lock,IX):表示事务打算对表中的各个行设置排它锁。

    例如,SELECT ... FOR UPDATE 会设置 IX 锁。

意向锁定协议如下:

  • 在事务可以获取表中行的共享锁之前,它必须首先获取表上的 IS 锁或更强的锁。

  • 在事务可以获取表中行的排他锁之前,它必须首先获取表上的 IX 锁。

表级锁类型兼容性如下:

锁的类型 X IX S IS
X \(\times\) \(\times\) \(\times\) \(\times\)
IX \(\times\) \(\checkmark\) \(\times\) \(\checkmark\)
S \(\times\) \(\times\) \(\checkmark\) \(\checkmark\)
IS \(\times\) \(\checkmark\) \(\checkmark\) \(\checkmark\)

注:\(\times\) 表示两种锁会冲突,\(\checkmark\) 表示两种锁会兼容。

如果事务请求的锁与现有的锁兼容,那么,请求的锁将会被授予,但如果它与现有的锁冲突,则请求的锁不会被授予。此时,事务将会等待,直到与现有锁冲突的锁被释放;如果请求的锁与现有锁冲突,并且由于会导致死锁,请求的锁将无法被授予,就会发生错误。

意向锁不会阻塞除全表锁定请求之外的其他任何锁定请求,即意向锁只会阻塞全表锁定请求,意向锁的主要目的是表明有人正在锁定一行,或者将要锁定表中的一行。

意向锁的事务数据可以通过 SHOW ENGINE INNODB STATUS 查看,执行后,会类似如下的输出:

TABLE LOCK table `test`.`t` trx id 10080 lock mode IX

记录锁

记录锁(record lock) 是索引记录上的锁。

例如,语句,可以防止任何其他事务插入、更新或删除值为 10 的 t.c1行。

SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE;

记录锁始终锁定索引记录,如果表定义时没有指定索引,InnoDB 会创建一个隐藏的聚集索引并使用该索引进行记录锁定。

记录锁的事务数据可以通过 SHOW ENGINE INNODB STATUS 查看,执行后,会类似如下的输出:

RECORD LOCKS space id 58 page no 3 n bits 72 index `PRIMARY` of table `test`.`t`
trx id 10078 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;;
 1: len 6; hex 00000000274f; asc     'O;;
 2: len 7; hex b60000019d0110; asc        ;;

间隙锁

间隙锁(gap lock) 是对索引记录之间间隙的锁定,或者对第一个索引记录之前或最后一个索引记录之后的间隙的锁定。间隙可能跨越单个索引值、多个索引值,甚至是空的。

例如,下面的语句可以防止其他事务将值为 15 的行插入到 t.c1 列中,无论该列中是否已经存在任何此类值,因为该范围内的所有现有值之间的间隙已被锁定。

SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE;

间隙锁是性能和并发性之间权衡的一部分,并且在某些事务隔离级别中可以使用,而在其他的事务隔离级别中不能使用。

对于使用通过唯一索引来搜索唯一行的查询语句,就不需要间隙锁定。如果是搜索条件中包含多列组合的唯一索引,就会出现间隙锁。

例如,如果 id 列有唯一索引,则以下语句仅使用索引上的记录锁,锁定值为 id 为 100 的行,此时,其他会话是否在前面的间隙中插入行并不重要:

SELECT * FROM child WHERE id = 100;

如果 id 列没有建立索引或具有非唯一索引,那么,该语句会锁定前面的间隙。

这里还需要注意的是,不同事务可以在间隙上持有冲突的锁。允许冲突间隙锁的原因是,如果需要从索引中清除一条记录,只需要将不同事务在该记录上持有的间隙锁合并即可。

例如,事务 A 可以在某个间隙上持有共享间隙锁(gap S-lock),而事务 B 在同一间隙上持有独占间隙锁(gap X-lock)。

InnoDB 间隙锁是“纯粹抑制性的”,这意味着它们的唯一目的是防止其他事务插入到间隙中,间隙锁可以共存,一个事务获取的间隙锁不会阻止另一事务在同一间隙上获取间隙锁。

共享间隙锁和独占间隙锁之间没有区别,它们彼此不冲突,并且执行相同的功能。

间隙锁定可以显式禁用,如果将事务隔离级别更改为 READ COMMITTED,索引查找和索引扫描会禁用间隙锁,此时,间隙锁仅用于外键约束检查和重复键检查。

使用 READ COMMITTED 隔离级别还有其他影响,MySQL 计算了 WHERE 条件后,会释放不匹配行的记录锁。

例如,对于 UPDATE 语句,InnoDB 会进行“半一致性”读取,从而将最新提交的版本返回给 MySQL,以便 MySQL 可以确定该行是否匹配该更新语句的 WHERE 条件。

Next-Key 锁

Next-Key Lock 是索引记录上的记录锁索引记录之前的间隙上的间隙锁的组合。

InnoDB 执行行级锁定的方式是,当它搜索或扫描表索引时,它会在遇到的索引记录上设置共享锁或独占锁,因此,行级锁实际上是索引记录锁。索引记录上的 Next-Key Lock 也会影响该索引记录之前的“间隙”,也就是说,Next-Key Lock 是索引的记录锁加上索引记录之前间隙上的间隙锁。因此,如果一个会话对索引中的记录 R 持有共享锁或独占锁,则另一个会话无法在索引顺序中紧邻 R 之前的间隙中插入新索引记录。

例如,假设索引列包含值 10、11、13 和 20,该索引可能的 Next-Key Lock 涵盖以下区间:

(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)

其中,圆括号表示排除区间端点,方括号表示包含端点。

对于最后一个间隔,Next-Key Lock 下一个键锁锁定索引中最大值之上的间隙,以及具有比索引中实际值更大的值的“最大”伪记录,上界不是真正的索引记录,因此,实际上,Next-Key Lock 仅锁定最大索引值之后的间隙。

在 REPEATABLE READ 事务隔离级别下运行,InnoDB 会使用 Next-Key Lock 进行搜索和索引扫描,这可以防止幻读。

Next-Key Lock 的事务数据可以通过 SHOW ENGINE INNODB STATUS 查看,执行后,会类似如下的输出:

RECORD LOCKS space id 58 page no 3 n bits 72 index `PRIMARY` of table `test`.`t`
trx id 10080 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;;
 1: len 6; hex 00000000274f; asc     'O;;
 2: len 7; hex b60000019d0110; asc        ;;

插入意向锁

插入意向锁(insert intention lock)是一种间隙锁,由 INSERT 操作在记录插入之前设置,如果插入同一索引间隙的多个事务,不会插入间隙内的同一个位置,则无需互相等待。

例如,假设存在值为 4 和 7 的索引记录,分别尝试插入值 5 和 6 的单独事务在获得插入行上的排他锁之前,每个事务都使用插入意向锁锁定 4 和 7 之间的间隙,但不会互相阻塞,因为行不冲突。

例如,以下示例演示了一个事务在获取插入记录上的排它锁之前获取插入意向锁。

客户端 A 创建一个包含两条索引记录(90和102)的表,然后启动一个事务,对 ID 大于100的索引记录设置排他锁,其中,排他锁会包括记录 102 之前的间隙锁:

mysql> CREATE TABLE child (id int(11) NOT NULL, PRIMARY KEY(id)) ENGINE=InnoDB;
mysql> INSERT INTO child (id) values (90),(102);

mysql> START TRANSACTION;
mysql> SELECT * FROM child WHERE id > 100 FOR UPDATE;
+-----+
| id  |
+-----+
| 102 |
+-----+

客户端 B 开启一个事务,尝试将 101 记录插入到间隙中,此时,该事务在等待获取排它锁时获取插入意向锁:

mysql> START TRANSACTION;
mysql> INSERT INTO child (id) VALUES (101);

插入意向锁的事务数据可以通过 SHOW ENGINE INNODB STATUS 查看,执行后,会类似如下的输出:

RECORD LOCKS space id 31 page no 3 n bits 72 index `PRIMARY` of table `test`.`child`
trx id 8731 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 80000066; asc    f;;
 1: len 6; hex 000000002215; asc     " ;;
 2: len 7; hex 9000000172011c; asc     r  ;;...

AUTO-INC 锁

锁AUTO-INC是插入到包含列的表中的事务所采用的特殊表级锁 AUTO_INCREMENT。在最简单的情况下,如果一个事务正在将值插入表中,则任何其他事务都必须等待才能向该表中执行自己的插入操作,以便第一个事务插入的行接收连续的主键值。

该innodb_autoinc_lock_mode 变量控制用于自动增量锁定的算法。它允许您选择如何在可预测的自动增量值序列和插入操作的最大并发度之间进行权衡。

空间索引的断言锁

InnoDB 支持 SPATIAL 包含空间数据的列的索引。

为了处理涉及 SPATIAL索引的操作的锁定,下一键锁定不能很好地支持REPEATABLE READ或 SERIALIZABLE事务隔离级别。多维数据中不存在绝对的排序概念,因此并不清楚哪个是 “下一个”键。

要支持带 SPATIAL索引的表的隔离级别,InnoDB 请使用谓词锁。索引SPATIAL包含最小边界矩形 (MBR) 值,因此 InnoDB通过在用于查询的 MBR 值上设置谓词锁来强制对索引进行一致读取。其他事务无法插入或修改与查询条件匹配的行。

标签:插入,事务,间隙,lock,模型,记录,索引,InnoDB
From: https://www.cnblogs.com/larry1024/p/17640613.html

相关文章

  • 星火大模型 VS FuncGPT(慧函数), 谁更胜一筹?
    哈喽,本文即通过相近的试题,看下最近爆火的科大讯飞星火大模型和FuncGPT(慧函数)的编码能力有何区别,给大家直观地对比。开发过程中经常会遇到读取文件内容的情况,需要【判断文件路径是目录还是文件】,及文件编码格式,防止无法读取内容或乱码出现情况。星火大模型生成代码示例如下:impor......
  • 鼎友餐饮信息总监杨山海:餐饮新增长依托数智应用,用数字化打造单店盈利模型
    杨山海鼎友餐饮信息总监近20年餐饮行业信息化、数字化决策经验,曾担任新辣道、雕刻时光、青年餐厅、快乐蜂、鲜果时间信数化负责人,主抓数字化转型、系统选型、多接口系统管理等,拥有丰富的餐饮行业数字化转型经验。2000年,杨山海从邯郸进京边学习边打工,经过杨闻钟老师指导和点拨,......
  • .net Core基础仓储模型
    .netCore简单仓储模型共分为三层:仓储层:Repository(类),IRepository(接口)业务层:Service(类),IService(接口)表现层:Controller(控制器接口层)如图所示: Repository层继承IRepositoryIbaseRepository接口里面写常用的增删改查方法(接口添加泛型及约束)为什么要用......
  • Spring-4-掌握Spring事务传播机制
    今日目标能够掌握Spring事务配置Spring事务管理1Spring事务简介【重点】1.1Spring事务作用事务作用:在数据层保障一系列的数据库操作同成功同失败Spring事务作用:在数据层或业务层保障一系列的数据库操作同成功同失败1.2案例分析Spring事务需求:实现任意两个账户间转......
  • 这,就是大模型时代的生产力!
    文心与飞桨,向我们展示了领先大模型的生产力。大模型应用卷到了什么地步?几天前,我们看到的还是写文章、画图、回答数学问题,现在已经有人这么用了:如果把一长段对话转发到别的群聊里,AI可以自动生成总结。拿到数据后,直接进行有理有据还配图的分析。从市场分析、品牌构建、到输出视频广......
  • 理性分析不同模型的性能指标
    性能指标FLOPS:浮点运算次数。MADD:表示一次乘法和一次加法,这可以粗略认为:MADD=2*Flops,即((输出一个元素所经历的乘法次数)+(输出一个元素所经历的加法的个数))*(输出总共的元素的个数)MEMREAD:网络运行时,从内存中读取的大小,即输入的特征图大小+网络参数的大小MEMWRITE:网络运行......
  • OpenCV3.3深度神经网络DNN模块 实例2:GoogleNet-Caffe模型实现图像分类
    1#include<opencv2/opencv.hpp>2#include<opencv2/dnn.hpp>3#include<iostream>4//使用GooglenetCaffe模型实现图像分类5usingnamespacecv;6usingnamespacecv::dnn;7usingnamespacestd;89Stringmodel_bin_file="D:/open......
  • OpenCV3.3深度神经网络DNN模块 实例3:SSD模型实现对象检测
    1#include<opencv2/opencv.hpp>2#include<opencv2/dnn.hpp>3#include<iostream>45usingnamespacecv;6usingnamespacecv::dnn;7usingnamespacestd;89constsize_twidth=300;//模型尺寸为300*30010constsize......
  • OpenCV3.3深度神经网络DNN模块 实例4:SSD-MobileNet模型实时对象检测
    1#include<opencv2/opencv.hpp>2#include<opencv2/dnn.hpp>3#include<iostream>45usingnamespacecv;6usingnamespacecv::dnn;7usingnamespacestd;89constsize_twidth=300;10constsize_theight=300;11cons......
  • OpenCV3.3深度神经网络DNN模块 实例5:FCN模型实现图像分割
    1#include<opencv2/opencv.hpp>2#include<opencv2/dnn.hpp>3#include<iostream>45usingnamespacecv;6usingnamespacecv::dnn;7usingnamespacestd;89constsize_twidth=300;10constsize_theight=30......