当前位置: 首页 > news >正文

MySQL 锁的详解:从 InnoDB 到死锁诊断实战

1.0 并发事务问题与两种基本解决方式

在数据库系统中,并发事务是常态。多个用户、多个业务线程同时对数据库进行读写,如果没有合理的控制,就会导致数据不一致的问题。本章将围绕事务并发带来的典型问题、两类基本解决方式(乐观并发控制与悲观并发控制)、以及在 MySQL InnoDB 中的具体表现展开。


1.1 并发事务的典型问题

定义

事务并发时,多个事务可能同时读写同一份数据,若不加控制,将出现以下异常:

  • 脏读(Dirty Read):读到未提交事务修改的数据。

  • 不可重复读(Non-repeatable Read):同一事务内多次读同一行,结果不同。

  • 幻读(Phantom Read):同一事务条件查询结果集中,前后出现了“多了/少了”行。

原理

这些现象来源于事务隔离的不同级别:

  • READ UNCOMMITTED:可能出现脏读。

  • READ COMMITTED:避免脏读,但仍可能出现不可重复读和幻读。

  • REPEATABLE READ(MySQL 默认):避免脏读、不可重复读,但可能出现幻读(依赖锁机制解决)。

  • SERIALIZABLE:最高隔离,所有事务串行执行,无以上问题,但并发性差。

示例(脏读)

-- 会话 A
BEGIN;
UPDATE account SET balance = balance - 100 WHERE id = 1;-- 会话 B
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN;
SELECT balance FROM account WHERE id = 1;

预期结果

  • 会话 B 在 READ UNCOMMITTED 下会看到会话 A 尚未提交的更新结果。

  • 若 A 最终回滚,则 B 读到的数据是“脏”的。


1.2 悲观并发控制(Pessimistic Concurrency Control)

定义

悲观控制假设冲突常发生,操作前主动加锁,确保在事务期间不被其他事务修改。

原理

  • 行级排他锁(Exclusive Lock, X Lock):阻塞其他读写。

  • 共享锁(Shared Lock, S Lock):允许多个读,但阻塞写。

示例(写-写冲突)

-- 会话 A
BEGIN;
UPDATE account SET balance = balance - 100 WHERE id = 1;-- 会话 B
BEGIN;
UPDATE account SET balance = balance - 50 WHERE id = 1;

预期结果

  • 会话 A 持有 id=1 行的 X 锁。

  • 会话 B 尝试获取 X 锁,阻塞等待。

  • 直到 A 提交或回滚后,B 才能继续。

诊断/优化建议

  • 可通过 SHOW ENGINE INNODB STATUSperformance_schema.data_locks 查看锁等待。

  • 悲观锁适合高冲突场景,但会降低并发度。


1.3 乐观并发控制(Optimistic Concurrency Control)

定义

乐观控制假设冲突少,读时不加锁,在写入时检查是否发生冲突,若冲突则回滚或重试。

原理

通常通过 版本号(version)时间戳 字段实现:

  • 每次更新检查版本号是否与读时一致。

  • 成功则更新并递增版本号;失败则说明有并发写入,需重试。

示例

-- 表定义增加版本号
ALTER TABLE account ADD COLUMN version INT DEFAULT 0;-- 会话 A 查询数据
SELECT id, balance, version FROM account WHERE id = 1;-- 会话 A 更新时检查版本
UPDATE account 
SET balance = balance - 100, version = version + 1
WHERE id = 1 AND version = 1;-- 会话 B 同时尝试更新
UPDATE account 
SET balance = balance - 50, version = version + 1
WHERE id = 1 AND version = 1;

预期结果

  • 只有一个事务会更新成功(影响行数 = 1)。

  • 另一个事务因版本号不匹配,影响行数 = 0,需要应用层重试。

诊断/优化建议

  • 乐观锁减少数据库锁开销,但需要业务逻辑支持重试。

  • 适用于“读多写少”的场景。


1.4 一致性读(Consistent Read / Snapshot Read)与 MVCC

定义

一致性读即快照读,基于 MVCC(Multi-Version Concurrency Control) 实现。

原理

  • 每个事务在启动时创建“Read View”。

  • InnoDB 通过 undo log 保存旧版本数据。

  • 查询时根据事务的 Read View 决定读取哪个版本。

示例

-- 会话 A
BEGIN;
UPDATE account SET balance = balance - 100 WHERE id = 1;-- 会话 B
BEGIN;
SELECT balance FROM account WHERE id = 1; -- 默认一致性读

预期结果

  • 即使会话 A 尚未提交,B 读到的仍是事务开始时的已提交版本。

  • 这体现了 MVCC 的快照隔离效果。

诊断/优化建议

  • 一致性读不加锁,性能好,适合大多数查询。

  • 若必须确保数据不被并发修改,应使用 锁定读


1.5 锁定读(Locking Read)

定义

锁定读会在读取数据的同时加锁,保证在事务期间数据不会被修改。

原理

  • SELECT ... FOR UPDATE:对读取的行加排他锁(X Lock)。

  • SELECT ... LOCK IN SHARE MODE(MySQL 5.7)/ FOR SHARE(MySQL 8.x):加共享锁(S Lock)。

示例

-- 会话 A
BEGIN;
SELECT balance FROM account WHERE id = 1 FOR UPDATE;-- 会话 B
BEGIN;
UPDATE account SET balance = balance - 50 WHERE id = 1;

预期结果

  • 会话 A 对 id=1 加排他锁。

  • 会话 B 的更新将被阻塞,直到 A 提交或回滚。

诊断/优化建议

  • 锁定读适用于需要“读后马上写”的业务逻辑。

  • 避免在事务内长时间持锁,防止阻塞其他事务。


1.6 写操作的锁行为

原理

写操作天然伴随锁:

  • INSERT:对新记录加排他锁,并可能加间隙锁避免幻读。

  • UPDATE / DELETE:对目标行加排他锁。

  • INSERT ... ON DUPLICATE KEY UPDATE:先加插入意向锁,再根据冲突情况加行锁。

示例(INSERT)

-- 会话 A
BEGIN;
INSERT INTO account(id, balance) VALUES(3, 500);-- 会话 B
BEGIN;
INSERT INTO account(id, balance) VALUES(3, 600);

预期结果

  • 会话 A 插入成功并持有 id=3 的隐式锁。

  • 会话 B 尝试插入相同主键,会被阻塞,直到 A 提交或回滚。

诊断/优化建议

  • 插入冲突常由唯一约束导致,合理设计索引可降低锁冲突。

  • 可通过 SHOW ENGINE INNODB STATUS 分析 insert intention lock。


小结

在第一章中,我们从并发事务的典型问题出发,介绍了 悲观并发控制乐观并发控制 两种思路,深入解释了 InnoDB 的 一致性读(MVCC)锁定读 行为,并结合具体 SQL 示例展示了不同场景下锁的产生与影响。

记忆要点

  • 悲观锁:先加锁再操作,适合冲突多场景。

  • 乐观锁:靠版本号/时间戳检测冲突,适合读多写少。

  • 一致性读:依赖 MVCC,无锁高效。

  • 锁定读:保障强一致,但降低并发。

  • 写操作:天然伴随锁,注意 insert intention 与唯一约束。

第 2 章 多粒度锁与意向锁

在 InnoDB 的并发控制机制中,锁是保证数据一致性与隔离性的重要手段。然而,如果数据库只提供最底层的 行级锁 或最粗粒度的 表级锁,在复杂的并发事务场景下会带来效率问题。因此,InnoDB 引入了 多粒度锁(Multiple Granularity Locking, MGL)意向锁(Intention Locks) 机制,解决了不同层级的锁之间协调与冲突检测的问题。

本章将详细介绍多粒度锁的基本思想、意向锁的原理、分类及在 InnoDB 中的实现方式。


2.1 多粒度锁的背景与动机

锁的作用在于保护数据对象不被并发事务破坏,但不同的锁粒度有不同的特点:

  • 表级锁:粒度大,加锁成本低,但并发度差。

  • 行级锁:粒度小,并发度高,但管理和冲突检测开销大。

在数据库系统中,我们往往既需要支持细粒度的高并发访问,又要能在必要时用粗粒度的锁来快速保护更大范围的数据对象。

于是产生了 多粒度锁 的思想:

  • 数据库对象形成 层次结构,例如:数据库 → 表 → 页 → 行

  • 事务可以在不同层级加锁(如整表加锁或只锁某一行)。

  • 系统需要一种机制来判断 上层对象的锁与下层对象的锁之间是否冲突

这就需要引入 意向锁


2.2 意向锁的设计原理

意向锁是一种 表级锁,用来表明事务在 更细粒度对象(如行)上加了什么类型的锁。

这样,数据库在检查表级锁与行级锁是否冲突时,就不必逐行扫描,而是只需先看表上的意向锁即可。

换句话说:

  • 意向锁是一种标志,表示事务在更低层级的数据上可能持有哪些锁。

  • 不会阻止其他事务的操作,但能帮助快速判断锁冲突。


2.3 意向锁的分类

InnoDB 中有两类意向锁:

  1. 意向共享锁(IS, Intention Shared Lock)

    • 表示事务准备在某些行上加 共享锁(S锁)

    • 其他事务可以继续在表上加共享锁,但如果有事务要加排他锁(X锁),则会冲突。

  2. 意向排他锁(IX, Intention Exclusive Lock)

    • 表示事务准备在某些行上加 排他锁(X锁)

    • 这种意向表明事务会修改数据,因此会和表级的 S 锁、X 锁冲突。

此外,还存在一个组合型:

  1. 共享意向排他锁(SIX, Shared with Intention Exclusive Lock)

    • 表示事务在表上持有 共享锁,同时在表的某些行上还会持有 排他锁

    • 它相当于 S + IX 的组合。


2.4 锁兼容性矩阵

为了直观理解,我们来看 InnoDB 中表级锁的兼容性(√ 表示兼容,× 表示冲突):

ISIXSSIXX
IS×
IX×××
S×××
SIX××××
X×××××

解释:

  • IS 与 IX 基本兼容:因为它们只是意向,不会直接冲突。

  • X 锁最强:与任何其他锁都不兼容。

  • SIX 比较特殊:只能和 IS 共存,其他情况都会冲突。


2.5 InnoDB 中的实现机制

在 InnoDB 中:

  • 当事务对某一行加锁时,会在 表级别 自动加上相应的意向锁。

    • 如果事务要对某行加 S 锁 → 在表上加 IS 锁。

    • 如果事务要对某行加 X 锁 → 在表上加 IX 锁。

  • 这一步是 自动完成 的,用户不需要显式申请意向锁。

这样,当另一个事务要对整个表加 S 或 X 锁时,只需检查表上的意向锁即可快速判断是否冲突,而不用扫描所有行锁。


2.6 应用场景与举例

场景 1:行锁与表锁的协调

  • 事务 A 在表 orders 的某一行上加了 X 锁(自动在表上有 IX 锁)。

  • 事务 B 想对整个 orders 表加 S 锁。

  • 此时 B 检查到表上已有 IX 锁,说明某些行已被事务修改 → 表级 S 锁申请失败。

场景 2:大范围操作的保护

  • 数据库管理员执行 LOCK TABLE orders READ;(即表级 S 锁)。

  • 此时所有对 orders 表行的 X 锁申请都会失败,因为表级 S 锁与 IX 锁冲突。


2.7 小结

本章我们学习了:

  1. 多粒度锁的背景:解决不同层级锁的冲突检测问题。

  2. 意向锁的作用:标记事务在更细粒度对象上的锁类型,提升冲突检测效率。

  3. 意向锁分类:IS、IX、SIX。

  4. 兼容性矩阵:X 锁最强,SIX 最特殊。

  5. InnoDB 实现:行锁自动伴随意向锁,用户无需手动干预。

理解多粒度锁和意向锁,对于我们深入学习 InnoDB 的并发控制和死锁分析至关重要。

第 3 章 行级锁:共享锁与排他锁

在 MySQL InnoDB 的锁机制中,行级锁(Row-level Lock) 是最常用、也是最核心的一类锁。相比于表级锁和页级锁,它粒度更小,能够显著提高并发度。但同时,行级锁的开销更大,容易产生死锁,需要深入理解其工作机制。


3.1 行级锁的定义与特点

  • 共享锁(Shared Lock, S Lock)

    • 允许事务读取一行记录,但不能修改。

    • 多个事务可以同时持有某行的共享锁。

    • 常见操作:SELECT ... LOCK IN SHARE MODE

  • 排他锁(Exclusive Lock, X Lock)

    • 允许事务对某行记录进行读写操作。

    • 在持有排他锁期间,其他事务既不能读取(共享锁),也不能写入(排他锁)。

    • 常见操作:SELECT ... FOR UPDATEUPDATEDELETEINSERT

特点

  1. 锁粒度最小:只锁定需要访问的记录,最大化并发。

  2. 锁冲突概率低:多个事务可访问不同行时互不影响。

  3. 加锁开销大:行级锁依赖 B+ 树索引定位,需要维护更多锁结构。

  4. 可能死锁:由于锁数量庞大,死锁概率比表锁更高。


3.2 行级锁的内部实现

InnoDB 使用 索引记录锁(Record Lock) 实现行级锁,依赖于聚簇索引(Clustered Index)。其内部实现分为以下几类:

  1. Record Lock

    • 直接锁定某条索引记录。

    • 例如:SELECT * FROM t WHERE id=10 FOR UPDATE; 会在 id=10 的索引项上加 排他锁

  2. Gap Lock(间隙锁)

    • 锁定索引记录之间的“间隙”,防止插入操作。

    • 常用于范围查询,以避免 幻读(Phantom Read)

  3. Next-Key Lock

    • 结合 Record LockGap Lock

    • 锁住记录本身 + 前一个间隙。

    • InnoDB 默认使用 Next-Key Lock 来避免幻读。

🔑 核心点:InnoDB 行锁 总是依赖索引

  • 如果查询条件没有命中索引,InnoDB 会退化成 表锁

  • 因此,合理使用索引是高并发的关键。


3.3 行级锁的加锁场景

不同 SQL 语句触发的锁类型:

  • 普通 SELECT:不加任何锁(MVCC 多版本控制保证一致性读)。

  • SELECT ... LOCK IN SHARE MODE:为命中行加 共享锁

  • SELECT ... FOR UPDATE:为命中行加 排他锁

  • INSERT/UPDATE/DELETE:自动加 排他锁

  • 范围查询(BETWEEN、>、<):会触发 Next-Key Lock

例子:

-- 为 id=5 的记录加共享锁
SELECT * FROM users WHERE id=5 LOCK IN SHARE MODE;-- 为 id=5 的记录加排他锁
SELECT * FROM users WHERE id=5 FOR UPDATE;-- 范围锁,阻止其他事务插入或修改 id 在 [10,20] 的记录
SELECT * FROM users WHERE id BETWEEN 10 AND 20 FOR UPDATE;

3.4 行级锁与死锁

由于行级锁粒度细、数量多,死锁 在 InnoDB 中是常见问题。

3.4.1 死锁的典型场景

  1. 两个事务互相持有对方需要的锁

    -- 事务A
    BEGIN;
    UPDATE users SET balance=balance-100 WHERE id=1;
    UPDATE users SET balance=balance+100 WHERE id=2;-- 事务B
    BEGIN;
    UPDATE users SET balance=balance-100 WHERE id=2;
    UPDATE users SET balance=balance+100 WHERE id=1;
    
    • A 先锁住 id=1,再等待 id=2。

    • B 先锁住 id=2,再等待 id=1。

    • 形成循环等待,导致死锁。

  2. 范围锁导致死锁

    • 当事务对范围查询加 Next-Key Lock 时,可能因为锁顺序不同而形成死锁。

3.4.2 InnoDB 死锁检测与处理

  • InnoDB 内部维护 等待图(Wait-for Graph),检测循环依赖。

  • 一旦检测到死锁,会回滚其中一个事务(一般是回滚代价最小的)。

命令查看死锁日志:

SHOW ENGINE INNODB STATUS\G

3.5 实践优化与注意事项

  1. 使用合适的索引

    • 行锁依赖索引,否则退化为表锁。

    • WHERE 条件必须命中索引。

  2. 尽量缩小锁范围

    • 避免大范围查询,如 SELECT ... WHERE id > 100

    • 使用更精确的条件。

  3. 避免锁升级

    • 在没有索引时,InnoDB 可能锁定全表。

  4. 保持锁顺序一致

    • 多事务操作相同资源时,应按相同顺序访问,避免死锁。

  5. 事务尽量短

    • 减少持锁时间,降低冲突概率。


3.6 小结

  • 行级锁是 InnoDB 并发控制的核心,分为 共享锁(S)排他锁(X)

  • 其实现基于 索引记录锁,常见形式有 Record Lock、Gap Lock、Next-Key Lock。

  • 行级锁能最大化并发,但容易出现死锁,需要合理设计 SQL 与事务。

  • 实践优化的关键在于 索引设计、事务粒度控制、访问顺序一致

第 4 章 间隙锁与 Next-Key Lock

在 InnoDB 的锁机制中,间隙锁(Gap Lock)Next-Key Lock 是两个非常重要的概念,它们的设计目标是 防止幻读(Phantom Read)。在事务的可重复读(REPEATABLE READ)隔离级别下,InnoDB 通过这两种锁实现了一种“悲观”的防护手段,以确保查询结果的稳定性。


4.1 间隙锁的概念与特性

间隙锁(Gap Lock) 是 InnoDB 中的一种特殊锁,它并不是锁住具体的某一行数据,而是 锁住两个索引记录之间的“间隙”

  • 目的:防止其他事务在这个间隙中插入新的记录,从而避免出现幻读。

  • 特点:

    1. 只阻止插入,不阻止查询

    2. 可并存,多个事务对同一个间隙加锁不会冲突。

    3. 仅在 非唯一索引 上生效。

🔎 举例:
假设一张表 t 有主键索引字段 id,记录为:

id: 10, 20, 30

事务 A 执行:

SELECT * FROM t WHERE id BETWEEN 15 AND 25 FOR UPDATE;

此时 InnoDB 不仅会锁住 id=20 这一行,还会在 (10, 20)(20, 30) 的间隙加上间隙锁,禁止其他事务在这些区间插入数据。


4.2 Next-Key Lock 的定义与应用

Next-Key Lock = 记录锁(Record Lock) + 间隙锁(Gap Lock)
它既锁定某一行数据,又锁定其前一个键值与该行之间的间隙。

  • 本质:保证读到的范围数据不会被插入新的行干扰。

  • 应用场景:Next-Key Lock 是 InnoDB 在 可重复读隔离级别 下的默认加锁方式。

🔎 举例:
继续用上例,事务 A 执行:

SELECT * FROM t WHERE id=20 FOR UPDATE;

此时 InnoDB 加上的锁是:

  • id=20 的记录锁

  • (10,20) 的间隙锁
    合起来就是一个 Next-Key Lock

这样就可以防止事务 B 在 (10,20) 或者恰好 id=20 的位置插入新数据。


4.3 为什么需要间隙锁与 Next-Key Lock?

问题的核心是 幻读(Phantom Read)

  • 幻读的定义:同一个事务中,前后两次执行相同条件的查询,结果集的记录数不同。

  • 根源:事务期间,其他事务插入了满足条件的新记录。

🔎 没有间隙锁时的幻读例子:

  1. 事务 A 执行:

    SELECT * FROM t WHERE id > 15 AND id < 25;
    

    得到结果:id=20

  2. 事务 B 插入新记录:

    INSERT INTO t VALUES(22);
    
  3. 事务 A 再次执行同样的查询,却得到 id=20,22,产生幻读。

👉 解决方案:事务 A 在第一次查询时,就对 (15,25) 区间加上 间隙锁Next-Key Lock,禁止插入操作。


4.4 不同隔离级别下的锁行为

隔离级别行为
READ COMMITTED不使用 Next-Key Lock,只在需要时对已存在的行加 记录锁。可能出现幻读。
REPEATABLE READ使用 Next-Key Lock,锁住行与间隙,避免幻读。
SERIALIZABLE对所有符合条件的范围加锁,甚至 SELECT 也可能被锁,代价最高。

4.5 间隙锁与死锁问题

间隙锁虽然防止了幻读,但也带来了更高的 死锁风险

  • 典型场景:两个事务在相邻区间加锁并尝试插入新数据时,可能互相等待,造成死锁。

  • 优化策略:

    1. 尽量使用 精确的唯一索引 查询,减少范围锁的产生。

    2. 控制事务中 SQL 的执行顺序,保持一致性。

    3. 如果业务允许,可以降级隔离级别到 READ COMMITTED


4.6 实战案例分析

案例 1:防止重复插入
假设有一张用户表,email 上有唯一索引。

SELECT * FROM users WHERE email='test@example.com' FOR UPDATE;

若记录不存在,InnoDB 会在 (前一个email, test@example.com] 区间加上 Next-Key Lock,从而防止并发事务插入相同的 email。

案例 2:范围查询防幻读

SELECT * FROM orders WHERE price BETWEEN 100 AND 200 FOR UPDATE;

事务 A 在执行后,会对整个 [100,200] 区间加锁,保证事务 B 无法插入新订单到该价格区间,避免事务 A 再次查询时出现幻读。


4.7 小结

  • 间隙锁:锁住索引间的空隙,只阻止插入,不阻止查询。

  • Next-Key Lock:记录锁 + 间隙锁,防止幻读,是 REPEATABLE READ 下的核心机制。

  • 目标:保证事务中查询结果的稳定性。

  • 代价:增加锁竞争与死锁概率,需要合理使用唯一索引和事务策略。

第 5 章 插入意向锁与并发插入

在 InnoDB 的锁机制体系中,除了常见的 行锁间隙锁Next-Key Lock 之外,还有一种比较特殊的锁——插入意向锁(Insert Intention Lock)
它并不是传统意义上阻止并发的锁,而是 InnoDB 为了 支持高并发插入操作 而设计的一种“协调机制”。理解插入意向锁,有助于我们准确把握 插入操作的并发控制逻辑,并学会在实际业务中避免死锁与性能问题。


5.1 插入意向锁的定义

  • 概念
    插入意向锁是一种在 插入记录之前,由事务自动生成的 特殊间隙锁(Gap Lock)
    它的作用是:

    • 声明意图:告诉 InnoDB 引擎,“我即将在某个索引间隙中插入新记录”。

    • 协调并发:允许多个事务在不同位置同时申请插入意向锁,从而避免在高并发场景下插入操作相互阻塞。

  • 本质
    插入意向锁 不会阻塞其他插入意向锁,但会与 对同一间隙的排他锁(Gap Lock / Next-Key Lock) 发生冲突。

换句话说,它是一种 弱锁:它不排他、不阻止其他插入操作,而只是保证“如果有别的事务持有间隙锁,则禁止我插入”。


5.2 插入意向锁的工作流程

当事务执行 INSERT 时,InnoDB 的加锁流程大致如下:

  1. 定位插入点
    InnoDB 会根据 聚簇索引二级索引 的有序性,找到插入新记录的合适间隙。

  2. 申请插入意向锁
    事务向该间隙加上一个 插入意向锁,标记“我准备在这里插入新行”。

  3. 检查间隙是否被锁住

    • 如果该间隙上有其他事务持有 Gap Lock / Next-Key Lock,那么插入操作会被阻塞。

    • 如果该间隙没有被锁,多个事务的插入意向锁可以 并行持有

  4. 实际插入记录
    插入新记录本身会对该行加上一个 排他锁(X Lock),保证插入记录的唯一性与安全性。


5.3 插入意向锁与其他锁的关系

锁类型是否阻塞插入意向锁说明
行级锁 (S/X)行锁只针对已存在的行,插入意向锁作用于间隙
Gap LockGap Lock 会阻止在该间隙插入新行
Next-Key LockNext-Key Lock 覆盖记录及间隙,也会阻止插入
插入意向锁否(彼此不冲突)多个事务可以同时持有,不会互相阻塞

👉 结论:插入意向锁只与间隙相关锁冲突,但不会与其他插入意向锁冲突。


5.4 并发插入(Concurrent Insert)

插入意向锁的存在,正是为了支持 InnoDB 的 并发插入机制

  • 目标:允许多个事务 同时向不同的间隙插入记录,而不是像早期数据库那样全表锁或行级阻塞。

  • 原理

    • 不同事务在不同间隙上加插入意向锁,互不影响。

    • 只有在“插入到同一个间隙”且“有其他间隙锁存在”时,才会发生等待。

  • 举例说明
    假设有一张表 t(id PK),现有记录为:1, 5, 10

    • 事务 A 想插入 3(间隙 1 < x < 5

    • 事务 B 想插入 7(间隙 5 < x < 10

    → 两个事务分别在不同间隙加插入意向锁,可以 并行插入,互不阻塞。

    但如果此时 事务 C 在间隙 (1, 5) 上加了一个 Gap Lock(比如执行了 SELECT * FROM t WHERE id BETWEEN 2 AND 4 FOR UPDATE),那么事务 A 的插入 3 就会被阻塞。


5.5 插入意向锁与死锁问题

插入意向锁虽然设计上是“弱锁”,但在某些情况下也可能导致 死锁

  • 死锁场景示例

    • 事务 A:尝试在 (1, 5) 插入 3

    • 事务 B:尝试在 (1, 5) 插入 4

    • 同时事务 C:在 (1, 5) 上加了 Gap Lock

    结果:A 和 B 都在等待 Gap Lock,而 C 可能在等待某个排他锁,从而出现死锁。

  • 避免办法

    • 保持事务操作的顺序一致,减少间隙锁的使用。

    • 避免长事务,减少间隙锁长时间持有。

    • 在高并发写入场景下,尽量让主键或索引分布均匀,减少多个事务争抢同一间隙。


5.6 实际应用与调优

  1. 高并发插入表设计

    • 使用 自增主键(AUTO_INCREMENT) 可以减少间隙锁竞争。

    • 但在 批量插入非连续插入 场景下,仍可能触发间隙冲突。

  2. 减少间隙锁影响

    • READ COMMITTED 隔离级别下,InnoDB 会 禁用 Gap Lock,从而插入更容易并发进行。

    • REPEATABLE READ 下,间隙锁才会生效,因此并发插入更容易阻塞。

  3. 业务层优化

    • 可以通过 分区表范围拆分,让插入更均匀,避免热点间隙。

    • 在一些写入密集型业务中,可以考虑 写入队列消息队列异步插入 来降低冲突。


5.7 小结

  • 插入意向锁 是一种特殊的间隙锁,用来标记事务的插入意图。

  • 它本身 不阻塞其他插入意向锁,但会与 Gap Lock / Next-Key Lock 冲突。

  • 借助插入意向锁,InnoDB 能够实现 高并发的插入操作,避免全局锁的性能瓶颈。

  • 在实际使用中,应注意事务隔离级别、间隙锁的使用以及主键设计,以减少死锁与阻塞风险。

http://www.dtcms.com/a/347289.html

相关文章:

  • Hive Metastore和Hiveserver2启停脚本
  • 爱普生打印机的使用
  • day40-tomcat
  • UE C++ 堆化
  • 【卫星通信】超低码率语音编码ULBC:EnCodec神经音频编解码器架构深度解析
  • 随机森林2——集成学习的发展
  • TCP:传输控制协议
  • JAVA核心基础篇-递归
  • C 语言标准输入输出头文件stdio.h及其常见用法
  • 【读论文】Qwen-Image技术报告解读
  • 云原生高级——K8S总概
  • ArkTS 语言全方位解析:鸿蒙生态开发新选择
  • 双指针:成最多水的容器
  • 使用 eventpp 构建跨 RT-Thread 与 ARM-Linux 的轻量级 Active Object(AO)事件驱动框架
  • AI Agent系列(十三) -智能体架构的真相
  • LoRA 微调
  • 探索 JUC:Java 并发编程的神奇世界
  • 单调栈详解
  • Vue环境组件node.js安装
  • C语言---数据类型
  • Qt中使用MySQL数据库
  • git实战(8)git高阶命令分析【结合使用场景】
  • ADC系统中的信噪比(SNR)
  • 容器安全实践(二):实践篇 - 从 `Dockerfile` 到 Pod 的权限深耕
  • 多模态医学图像融合:解锁顶会顶刊中的医学影像新视界
  • 【GaussDB】使用MySQL客户端连接到GaussDB的M-Compatibility数据库
  • 智慧零售漏扫率↓79%!陌讯多模态融合算法在智能收银与货架管理的实战解析
  • 【Linux】深度学习Linux下的包管理器yum/apt
  • Day22: Python涡轮增压计划:用C扩展榨干最后一丝性能!
  • 微前端架构常见框架