MySQL间隙锁与排他锁的区别全解析

 更新时间:2026年01月30日 09:26:56   作者:伐尘  
本文给大家介绍了MySQL间隙锁与排他锁的区别,文章提到了间隙锁在实际工作中的应用场景和注意事项,建议根据具体需求选择合适的锁机制,感兴趣的朋友跟随小编一起看看吧

【MySQL】间隙锁 与 排他锁 的区别

这是一个非常核心的数据库锁机制问题。简单来说:排他锁(X锁)是"锁什么",间隙锁(Gap Lock)是"锁哪里"

📊 核心区别对比表

特性排他锁 (X锁)间隙锁 (Gap Lock)
锁的类型锁类型 - 定义锁的"权限"锁范围 - 定义锁的"位置"
作用对象已存在的数据行数据行之间的间隙
兼容性与任何其他锁都不兼容与其他间隙锁兼容,但与插入意向锁冲突
主要目的防止其他事务读写数据防止其他事务在范围内插入新数据
可见范围行记录本身索引记录之间的"空隙"
何时使用UPDATE、DELETE、SELECT…FOR UPDATE可重复读隔离级别下的范围查询

🔍 详细解释

1. 排他锁(X锁)- 行级锁

-- 示例:对uid=8的行加排他锁
SELECT * FROM student WHERE uid = 8 FOR UPDATE;
-- 或
UPDATE student SET age = 60 WHERE uid = 8;

特点:

  • 锁定具体的数据行
  • 其他事务不能读取(SELECT…FOR UPDATE)也不能修改(UPDATE/DELETE)该行
  • 可以看作"写锁"

2. 间隙锁(Gap Lock)- 范围锁

-- 示例:锁定uid在(5, 10)之间的所有间隙
SELECT * FROM student WHERE uid BETWEEN 6 AND 9 FOR UPDATE;
-- 或
SELECT * FROM student WHERE uid > 5 AND uid < 10 FOR UPDATE;

假设你的表数据:

uid: 1, 2, 3, 4, 5, 7, 8, 10, 11

间隙包括:(-∞,1), (1,2), (2,3), …, (5,7), (7,8), (8,10), (10,11), (11,+∞)

特点:

  • 锁定索引记录之间的空隙,而不是记录本身
  • 防止其他事务在范围内插入新数据
  • 只在可重复读(REPEATABLE-READ) 隔离级别下有效

🎯 实际工作场景

场景1:防止幻读(Phantom Read)

-- 事务一
BEGIN;
SELECT * FROM student WHERE age BETWEEN 20 AND 30 FOR UPDATE;
-- 锁定所有age在20-30之间的间隙
-- 事务二尝试插入age=25的新记录(会被阻塞)
INSERT INTO student (name, age, sex) VALUES ('new_student', 25, 'M');

场景2:组合使用 - 临键锁(Next-Key Lock)

MySQL 实际上经常使用 **临键锁 = 记录锁 + 间隙锁**
-- 锁定uid=8的行及其前后的间隙
SELECT * FROM student WHERE uid = 8 FOR UPDATE;
-- 实际锁定:(7, 8] 和 (8, 10)

⚠️ 重要注意事项

1. 间隙锁只作用于非唯一索引

-- 如果name有唯一索引,不会加间隙锁
SELECT * FROM student WHERE name = 'bbbb' FOR UPDATE;
-- 只对'bbbb'这一行加排他锁
-- 如果name没有索引或不是唯一索引,可能会锁全表间隙
SELECT * FROM student WHERE name LIKE 'b%' FOR UPDATE;

2. 查看锁信息

-- 查看当前的锁(MySQL 8.0+)
SELECT 
    engine_transaction_id as trx_id,
    lock_type,
    lock_mode,
    lock_data,
    index_name
FROM performance_schema.data_locks 
WHERE object_name = 'student';

3. 死锁风险

间隙锁容易导致死锁,特别是并发插入时:

-- 事务A
BEGIN;
SELECT * FROM student WHERE uid = 15 FOR UPDATE; -- 间隙锁(10, +∞)
-- 事务B  
BEGIN;
SELECT * FROM student WHERE uid = 16 FOR UPDATE; -- 间隙锁(10, +∞)
-- 事务A
INSERT INTO student (uid, name) VALUES (15, 'test'); -- 等待B
-- 事务B
INSERT INTO student (uid, name) VALUES (16, 'test'); -- 等待A → 死锁!

🔧 性能影响与优化

情况影响解决方案
全表间隙锁锁住所有间隙,并发极差使用唯一索引查询
范围查询锁住大范围间隙缩小查询范围
批量操作可能锁大量间隙分批次操作
高并发插入容易死锁降低隔离级别为RC

💡 最佳实践建议

明确使用场景:

需要防止幻读 → 使用间隙锁(RR隔离级别)

只需要防止数据修改 → 使用行级排他锁

索引设计:

-- 好的设计:使用唯一索引避免不必要的间隙锁
CREATE UNIQUE INDEX idx_uid ON student(uid);
-- 查询时使用唯一索引
SELECT * FROM student WHERE uid = 8 FOR UPDATE; -- 只锁一行

隔离级别选择:

-- 如果不需要防止幻读,使用读已提交
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 此时不会加间隙锁

避免长事务:

-- 尽快提交事务,减少锁持有时间
BEGIN;
-- 立即执行需要加锁的操作
SELECT ... FOR UPDATE;
UPDATE ...;
-- 尽快提交
COMMIT;

📝 总结要点

  1. 排他锁是"行锁" - 锁已存在的数据
  2. 间隙锁是"范围锁" - 锁不存在的间隙
  3. MySQL 默认使用 临键锁(行锁+间隙锁)
  4. 间隙锁主要在 RR隔离级别 防止幻读
  5. 设计好索引可以大大减少不必要的间隙锁

在你的学生表例子中,如果 uid 是唯一索引,SELECT ... WHERE uid=8 FOR UPDATE 只会加行锁。如果 name 不是唯一索引,相同的查询可能会加间隙锁。

到此这篇关于MySQL间隙锁与排他锁的区别全解析的文章就介绍到这了,更多相关mysql间隙锁与排他锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • mysql 5.7.18 安装配置方法图文教程(CentOS7)

    mysql 5.7.18 安装配置方法图文教程(CentOS7)

    这篇文章主要为大家详细介绍了CentOS 7下mysql 5.7.18 安装配置方法图文教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-04-04
  • MySQL8.0数据库开窗函数图文详解

    MySQL8.0数据库开窗函数图文详解

    开窗函数为将要被操作的行的集合定义一个窗口,它对一组值进行操作,不需要使用GROUP BY子句对数据进行分组,能够在同一行中同时返回基础行的列和聚合列,这篇文章主要给大家介绍了关于MySQL8.0数据库开窗函数的相关资料,需要的朋友可以参考下
    2023-06-06
  • 一文带你了解MySQL之InnoDB统计数据是如何收集的

    一文带你了解MySQL之InnoDB统计数据是如何收集的

    通过show index可以看到关于索引的统计数据,那么这些统计数据是怎么来的呢,它们是以什么方式收集的呢,本章将聚焦于InnoDB存储引擎的统计数据收集策略,需要的朋友可以参考下
    2023-05-05
  • Mysql命令行导入sql数据的代码

    Mysql命令行导入sql数据的代码

    Mysql命令行导入sql数据的实现方法是我们经常会用到的,下面就为你详细介绍Mysql命令行导入sql数据的方法步骤,希望对您学习Mysql命令行方面能有所帮助。
    2010-12-12
  • MySQL INSERT 导致的死锁问题分析及解决方案

    MySQL INSERT 导致的死锁问题分析及解决方案

    本文分析MySQL8.4.6中并发INSERT导致的死锁,指出因间隙锁(由外键约束和主键UUID引发)及页分裂操作形成循环等待,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2025-08-08
  • mysql5.7.22 下载过程图解

    mysql5.7.22 下载过程图解

    这篇文章主要介绍了mysql5.7.22 下载过程图解,非常不错,具有参考价借鉴价值,需要的朋友可以参考下
    2018-05-05
  • 关于MySQL日期类型的选择建议

    关于MySQL日期类型的选择建议

    在软件开发中,时间记录是不可或缺的功能,如记录操作时间、交易时间等,通常不建议使用字符串存储日期,因为它占用空间大,并且效率低下,MySQL提供的Datetime和Timestamp是常用的时间存储类型,Datetime没有时区信息,而Timestamp与时区有关
    2024-10-10
  • MYSQL关联关系查询方式

    MYSQL关联关系查询方式

    文章详细介绍了MySQL中如何使用内连接和左外连接进行表的关联查询,并展示了如何选择列和使用别名,文章还提供了一些关于查询优化的建议,并鼓励读者参考和支持脚本之家
    2025-02-02
  • Mysql一对多轻松实现追踪历史首条记录

    Mysql一对多轻松实现追踪历史首条记录

    本文介绍了在数据库中处理一对多关系时,如何使用JOIN和WHERE子句来找到每个主表记录对应的子表中特定记录(例如时间最早的记录),通过将B表与自身进行比较并使用MIN()函数,可以精确匹配到所需记录
    2024-12-12
  • 分组查询GROUP BY的使用与SQL执行顺序的讲解

    分组查询GROUP BY的使用与SQL执行顺序的讲解

    今天小编就为大家分享一篇关于分组查询GROUP BY的使用与SQL执行顺序的讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-03-03

最新评论