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

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

文章目录

  • [【MySQL】间隙锁 与 排他锁 的区别](#【MySQL】间隙锁 与 排他锁 的区别)
    • [📊 核心区别对比表](#📊 核心区别对比表)
    • [🔍 详细解释](#🔍 详细解释)
      • [1. 排他锁(X锁)- 行级锁](#1. 排他锁(X锁)- 行级锁)
      • [2. 间隙锁(Gap Lock)- 范围锁](#2. 间隙锁(Gap Lock)- 范围锁)
    • [🎯 实际工作场景](#🎯 实际工作场景)
      • [场景1:防止幻读(Phantom Read)](#场景1:防止幻读(Phantom Read))
      • [场景2:组合使用 - 临键锁(Next-Key Lock)](#场景2:组合使用 - 临键锁(Next-Key Lock))
    • [⚠️ 重要注意事项](#⚠️ 重要注意事项)
      • [1. 间隙锁只作用于非唯一索引](#1. 间隙锁只作用于非唯一索引)
      • [2. 查看锁信息](#2. 查看锁信息)
      • [3. 死锁风险](#3. 死锁风险)
    • [🔧 性能影响与优化](#🔧 性能影响与优化)
    • [💡 最佳实践建议](#💡 最佳实践建议)
    • [📝 总结要点](#📝 总结要点)

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

📊 核心区别对比表

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

🔍 详细解释

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

sql 复制代码
-- 示例:对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)- 范围锁

sql 复制代码
-- 示例:锁定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;

假设你的表数据:

sql 复制代码
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)

sql 复制代码
-- 事务一
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)

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

⚠️ 重要注意事项

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

sql 复制代码
-- 如果name有唯一索引,不会加间隙锁
SELECT * FROM student WHERE name = 'bbbb' FOR UPDATE;
-- 只对'bbbb'这一行加排他锁

-- 如果name没有索引或不是唯一索引,可能会锁全表间隙
SELECT * FROM student WHERE name LIKE 'b%' FOR UPDATE;

2. 查看锁信息

sql 复制代码
-- 查看当前的锁(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. 死锁风险

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

sql 复制代码
-- 事务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

💡 最佳实践建议

  1. 明确使用场景:

    • 需要防止幻读 → 使用间隙锁(RR隔离级别)
    • 只需要防止数据修改 → 使用行级排他锁
  2. 索引设计:

    sql 复制代码
    -- 好的设计:使用唯一索引避免不必要的间隙锁
    CREATE UNIQUE INDEX idx_uid ON student(uid);
    
    -- 查询时使用唯一索引
    SELECT * FROM student WHERE uid = 8 FOR UPDATE; -- 只锁一行
  3. 隔离级别选择:

    sql 复制代码
    -- 如果不需要防止幻读,使用读已提交
    SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
    -- 此时不会加间隙锁
  4. 避免长事务:

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

📝 总结要点

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

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

相关推荐
那个松鼠很眼熟w3 分钟前
3.Statement对象概述,以及Statement的弊端
数据库
山岚的运维笔记1 小时前
SQL Server笔记 -- 第72章:隔离级别与锁定
数据库·笔记·后端·sql·microsoft·sqlserver
硅基动力AI1 小时前
如何判断一个关键词值不值得做?
java·前端·数据库
新缸中之脑2 小时前
从零实现AI代理的长期记忆
数据库·人工智能
AC赳赳老秦2 小时前
文旅AI趋势:DeepSeek赋能客流数据,驱动2026智慧文旅规模化跃迁
人工智能·python·mysql·安全·架构·prometheus·deepseek
清水白石0083 小时前
Fixture 的力量:pytest fixture 如何重新定义测试数据管理
数据库·python·pytest
Rick19934 小时前
如何保证数据库和Redis缓存一致性
数据库·redis·缓存
那个松鼠很眼熟w4 小时前
2.获取数据库连接
数据库
_ziva_6 小时前
5 分钟搭建 CSV 数据问答系统:LangChain + LLM 实战教程
jvm·数据库·oracle
JiaHao汤7 小时前
MySQL SQL 性能优化实战指南
sql·mysql·性能优化