【行锁及其算法、死锁、意向锁】

本文详细解读了InnoDB存储引擎的三种行锁算法:RecordLock、GapLock和Next-KeyLock,以及它们在事务隔离和死锁预防中的作用。特别关注了Next-KeyLock在唯一属性和多索引场景下的行为,以及意向锁在支持多粒度锁定中的关键作用。

廖志伟的自我介绍

InnoDB 行锁的3种算法

InnoDB存储引擎有3种行锁的算法,其分别是:
❑Record Lock:单个行记录上的锁
❑Gap Lock:间隙锁,锁定一个范围,但不包含记录本身
❑Next-Key Lock∶Gap Lock+Record Lock,锁定一个范围,并且锁定记录本身

Record Lock总是会去锁住索引记录,如果InnoDB存储引擎表在建立的时候没有设置任何一个索引,那么这时 InnoDB存储引擎会使用隐式的主键来进行锁定。 Next-Key Lock是结合了Gap Lock和Record Lock的一种锁定算法,在Next-Key Lock算法下,InnoDB对于行的查询都是采用这种锁定算法。

在InnoDB 默认的事务隔离级别下,即REPEATABLE READ下,InnoDB存储引擎采用Next-Key Locking这种锁定 算法。例如一个索引有10,11,13和20这四个值,那么该索引可能被Next-Key Locking的区间为: (-∞,10] (10,11] (11,13] (13,20] (20,+∞)

当查询的索引含有唯一属性时,InnoDB存储引擎会对Next-Key Lock进行优化,将其降级为 Record Lock,即仅锁住索引本身,而不是范围。 什么是唯一属性,其实就是我们所说的能够标识该行数据唯一的标识。 unique 字段。比如:主键就是唯一的,不重复的。我们也可以自己设计多个字段组合不重 复,唯一的。

DROP TABLE IF EXISTS t;
CREATE TABLE t(a INT PRIMARY KEY);
INSERT INTO t SELECT 1;
INSERT INTO t SELECT 2;
INSERT INTO t SELECT 5;

表t共有1、2、5三个值。在上面的例子中,在会话A中首先对a=5进行X锁定。而由于a是主 键且唯一,因此锁定的仅是5这个值,而不是(2,5)这个范围,这样在会话B中插入值4而不 会阻塞,可以立即插入并返回。即锁定由Next-Key Lock算法降级为了Record Lock,从而提高应用的并发性。

CREATE TABLE z(a INT,b INT, PRIMARY KEY(a),KEY(b));
INSERT INTO z SELECT 1,1;
INSERT INTO z SELECT 3,1;
INSERT INTO z SELECT 5,3;
INSERT INTO z SELECT 7,6;
INSERT INTO z SELECT 10,8;

表z的列b是辅助索引,若在会话A中执行下面的SQL语句: SELECT * FROM z WHERE b=3 FOR UPDATE 很明显,这时SQL语句通过索引列b进行查询,该列不是唯一属性,因此其使用传统的Next-Key Locking技术加锁,并且由于有两个索引,其需要分别进行锁定。对于聚集索引(primay-key a),其仅对列a等于5的索引加 上Record Lock。而对于辅助索引b,其加上的是Next- Key Lock,锁定的范围是(1,3)。特别需要注意的是, InnoDB存储引擎还会对辅助索引下一个键值加上gap lock,即还有一个辅助索引范围为(3,6)的锁。

SELECT * FROM z WHERE a = 5 LOCK IN SHARE MODE;
INSERT INTO z SELECT 4,2;
INSERT INTO Z SELECT 6,5;

第一个SQL语句不能执行,因为在会话A中执行的 SQL语句已经对聚集索引中列a=5的值加上X锁,因 此执行会被阻塞。
第二个SQL语句,主键插入4,没 有问题,但是插入的辅助索引值2在锁定的范围(1, 3)中,因此执行同样会被阻塞。第三个SQL语句, 插入的主键6没有被锁定,5也不在范围(1,3)之间。 但插入的值5在另一个锁定的范围(3,6)中,故同样 需要等待。

InnoDB 行锁 next-key lock 有什么作用

在默认的事务隔离级别下,即REPEATABLE READ下,InnoDB存储引擎采用Next-Key Locking机制来避免 Phantom Problem(幻读问题,也称不可重复读)。Phantom Problem是指在同一事务下,连续执行两次同 样的SQL语句可能导致不同的结果。(在 READ COMMITTED 事务隔离级别下会出现)

CREATE TABLE z(a INT,b INT, PRIMARY KEY(a),KEY(b));
INSERT INTO z SELECT 1,1;
INSERT INTO z SELECT 3,1;
INSERT INTO z SELECT 5,3;
INSERT INTO z SELECT 7,6;
INSERT INTO z SELECT 10,8;

在同一事务中,若此时执行语句: SELECT*FROM z WHERE b=3 FOR UPDATE 两次,中间间隔10秒时间执行。可以肯 定的说,我们会得到第三行数据的结果,即 (5,3)。此时我们知道,会有一个 Record Lock锁定主键 5,还会有一个 gap lock锁定 (1,3)和 (3,6)。

假设:我们分析下,若此时没有gap lock(1,3)和 (3,6),如果只有Record Lock锁定主键 5 会不会造成幻读。 分析:我们在第一次 select 完成之后,第二次select 之前,插入一天数据: INSERT INTO z SELECT 20,3; 这条数据是可 以插入成功的,因为我们只有一个record lock 锁定了 主键5,对于新插入的数据主键为 20 ,可以插入,且 无重复。 插入完成后,第二次 select 得到了两个值,(5,3)(20,3)。这就造成了同一事物中,第一次读取和第二次 读取的结果不一样,出现幻读。 如果有gap lock,插入就会被阻塞,不会出现幻读。

死锁

如何预防数据库死锁,生产环境如何避免死锁 MySQL 篇 死锁是指两个或两个以上的事务在执行过程中,因争夺锁资源而造成的一种互相等待的现象。若无外力作用,事 务都将无法推进下去。解决死锁问题最简单的方式是不要有等待,将任何的等待都转化为回滚,并且事务重新开 始。毫无疑问,这的确可以避免死锁问题的产生。然而在线上环境中,这可能导致并发性能的下降,甚至任何一 个事务都不能进行。而这所带来的问题远比死锁问题更为严重,因为这很难被发现并且浪费资源。 解决死锁问题最简单的一种方法是超时,即当两个事务互相等待时,当一个等待时间超过设置的某一阈值时,其 中一个事务进行回滚,另一个等待的事务就能继续进行。在InnoDB存储引擎中,参数innodb_lock_wait_timeout用 来设置超时的时间。 超时机制虽然简单,但是其仅通过超时后对事务进行回滚的方式来处理,或者说其是根据FIFO的顺序选择回滚对 象。但若超时的事务所占权重比较大,如事务操作更新了很多行,占用了较多的undo log,这时采用FIFO的方式, 就显得不合适了,因为回滚这个事务的时间相对另一个事务所占用的时间可能会很多。 因此,除了超时机制,当前数据库还都普遍采用wait-for graph(等待图)的方式来进行死锁检测。较之超时的解 决方案,这是一种更为主动的死锁检测方式。InnoDB存储引擎也采用的这种方式。wait-for graph要求数据库保存以下两种信息: 锁的信息链表和事务等待链表。

事务T1指向T2边的定义为:
❑事务T1等待事务T2所占用的资源
❑事务T1最终等待T2所占用的资源,也就是事务之间在等待相同的资源,而事务T1发生在事务T2的后面
wait-for graph是一种较为主动的死锁检测机制,在每个事务请求锁并发生等待时都会判断是否存在回路,若存在则有死锁,通常来说InnoDB存储引擎选择回滚undo量最小的事务。

意向锁

InnoDB存储引擎支持多粒度(granular)锁定,这种锁定允许事务在行级上的锁和表级上的锁同时存在。 为了支持在不同粒度上进行加锁操作,InnoDB存储引擎支持一种额外的锁方式,称之为意向锁(Intention Lock)。 意向锁是将锁定的对象分为多个层次,意向锁意味着事务希望在更细粒度(fine granularity)上进行加锁 InnoDB存储引擎支持意向锁设计比较简练,其意向锁即为表级别的锁。设计目的主要是为了在一个事务中揭示下 一行将被请求的锁类型。其支持两种意向锁:
1)意向共享锁(IS Lock),事务想要获得一张表中某几行的共享锁
2)意向排他锁(IX Lock),事务想要获得一张表中某几行的排他锁
由于InnoDB存储引擎支持的是行级别的锁,因此意向锁其实不会阻塞除全表扫以外的任何请求。

InnoDB存储引擎实现了如下两种标准的行级锁:
❑共享锁(S Lock),允许事务读一行数据。
❑排他锁(X Lock),允许事务删除或更新一行数据。

如果一个事务T1已经获得了行r的共享锁,那么另外的事务T2可以立即获得行r的共享锁,因为读取并 没有改变行r的数据,称这种情况为锁兼容(Lock Compatible)。但若有其他的事务T3想获得行r的排 他锁,则其必须等待事务T1、T2释放行r上的共享锁——这种情况称为锁不兼容。

<think>我们正在回答用户的问题:“我想了解MySQL模式 请问MySQL模式介绍”。根据之前的对话历史,用户可能是在调试存储过程时遇到了的问题,现在想深入了解MySQL机制。 结合引用内容,我们需要介绍MySQL模式,尤其是InnoDB存储引擎的模式。 引用中提到了: 引用[1]:关于innodb_autoinc_lock_mode,这是自增的模式,属于表级的一种特殊情况。 引用[2]:行级锁定是由存储引擎实现的,如InnoDB和NDBCluster,我们将重点介绍InnoDB。 引用[3]:排他(X)和共享(S)的概念,以及SELECT ... FOR UPDATE的语法。 引用[4]:MySQL源码中的模式枚举,包括LOCK_IS, LOCK_IX, LOCK_S, LOCK_X, LOCK_AUTO_INC等。 因此,我们主要介绍InnoDB的模式,包括: 1. 共享(S)和排他(X)——行级锁的基本模式。 2. 意向锁(Intention Locks)——表级,包括意向共享(IS)和意向排他(IX)。 3. 自增(AUTO-INC Locks)——表级,用于自增列。 4. 记录(Record Locks)、间隙(Gap Locks)、临键(Next-Key Locks)——行级锁的三种算法,但它们属于算法,而模式主要是S和X。 注意:模式是指的属性(共享、排他等),而算法是指的范围(记录、间隙等)。 我们还需要注意,用户可能对的兼容性感兴趣,所以可以给出的兼容性矩阵。 回答结构: 1. 引言:介绍MySQL模式的重要性以及InnoDB作为重点。 2. 行级锁的基本模式: - 共享(S) - 排他(X) 3. 意向锁(表级): - 意向共享(IS) - 意向排他(IX) 4. 自增(AUTO-INC Locks) 5. 的兼容性 6. 总结 注意:在回答中适当引用参考文献,例如在介绍自增时引用[1],在介绍时引用[2][3][4]。 </think>### MySQL 模式详解 MySQL模式主要分为 **表级** 和 **行级锁** 两大类,不同存储引擎支持的模式有所差异。以下是核心模式的系统分析: --- #### 一、表级模式 适用于 MyISAM 等引擎,定整个表: 1. **共享 (S Lock)** - 允许多个会话同时读取数据 - 语法示例:`LOCK TABLES table_name READ` - 兼容性:允许其他会话加 S ,禁止加 X [^3] 2. **排他 (X Lock)** - 独占访问权限,禁止其他任何 - 语法示例:`LOCK TABLES table_name WRITE` - 兼容性:阻塞所有其他请求[^3] 3. **自增 (AUTO-INC Lock)** - 保证自增字段的连续性 - 通过 `innodb_autoinc_lock_mode` 配置为: - `0`:传统模式(全表) - `1`:连续模式(默认) - `2`:交错模式(最高并发)[^1] --- #### 二、行级锁模式 (InnoDB 实现) 支持更细粒度的并发控制: 1. **共享 (S Lock)** - 允许多事务并发读取同一 - 语法:`SELECT ... LOCK IN SHARE MODE` - 兼容性:允许其他 S ,禁止 X [^4] 2. **排他 (X Lock)** - 独占访问权限 - 语法:`SELECT ... FOR UPDATE` - 兼容性:阻塞所有其他请求[^3] 3. **意向锁 (Intention Locks)** - 表级,标识即将施加的类型: - `IS`(意向共享):将施加 S - `IX`(意向排他):将施加 X - 作用:避免表级冲突[^4] ```mermaid graph TD A[表级] --> B[共享 S] A --> C[排他 X] A --> D[自增 AUTO-INC] E[行级锁] --> F[共享 S] E --> G[排他 X] E --> H[意向锁 IS/IX] ``` --- #### 三、兼容性矩阵 | 请求 \ 已存在 | IS | IX | S | X | |-------------------|----------|----------|----------|----------| | IS | ✓ | ✓ | ✓ | ✗ | | IX | ✓ | ✗ | ✗ | ✗ | | S | ✓ | ✗ | ✓ | ✗ | | X | ✗ | ✗ | ✗ | ✗ | > ✓ 表示兼容,✗ 表示冲突[^4] --- #### 四、实践建议 1. **优先使用行级锁** 在事务中通过 `SELECT ... FOR UPDATE` 定关键,减少竞争[^2] 2. **控制粒度** 批量操作时使用批次提交(如 `LIMIT 200000`),避免长事务阻塞 3. **监控争用** ```sql SHOW ENGINE INNODB STATUS; -- 查看等待 SELECT * FROM information_schema.INNODB_LOCKS; -- 查看活跃 ``` --- #### 五、总结 MySQL 模式的核心是通过 **共享(S)** 实现并发读,通过 **排他(X)** 保证写安全,辅以 **意向锁(IS/IX)** 提升效率。实际应用中需根据场景权衡粒度,InnoDB 的行级锁在多数高并发场景更优[^2][^4]。 --- ### 相关问题 1. InnoDB 的间隙(Gap Lock)如何解决幻读问题? 2. 如何优化 `SELECT ... FOR UPDATE` 在百万级数据表的性能? 3. 什么是死锁检测(Deadlock Detection)?MySQL 如何处理死锁? 4. 在读写分离架构中,行级锁如何影响主从同步? 5. 自增模式 `innodb_autoinc_lock_mode=2` 的适用场景和风险? 6. 如何通过索引设计减少冲突? [^1]: 自增模式配置说明 [^2]: InnoDB 行级锁实现原理 [^3]: 排他与共享特性 [^4]: MySQL 源码模式定义
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java程序员廖志伟

赏我包辣条呗

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值