MySQL行锁、间隙锁和临键锁示例详解

 更新时间:2025年11月10日 09:59:59   作者:tanxinji  
InnoDB存储引擎通过行锁、间隙锁和临键锁实现MVCC和基于锁的并发控制,确保事务隔离级别和避免死锁,本文给大家介绍MySQL行锁、间隙锁和临键锁,感兴趣的朋友跟随小编一起看看吧

在MySQL中,InnoDB存储引擎实现了其自己的多版本并发控制(MVCC)机制,以及基于锁的并发控制。InnoDB使用锁来实现事务的隔离级别,其中包括行锁(Row Locks)、间隙锁(Gap Locks)和临键锁(Next-Key Locks)。这些锁的类型对于理解事务的隔离级别和避免死锁等问题非常重要。

1.行锁 (Record Lock)

锁住索引中的一条具体记录。 保证原子性:防止多个事务同时修改同一条记录
例如:SELECT * FROM t WHERE id = 10 FOR UPDATE;会在 id=10这条记录的索引上加行锁。

示例

-- 事务A
BEGIN;
SELECT * FROM users WHERE id = 10 FOR UPDATE;
-- 此时其他事务执行以下操作会被阻塞:
-- UPDATE users SET name = 'new' WHERE id = 10;
-- DELETE FROM users WHERE id = 10;

2.间隙锁(Gap Lock)

锁住索引记录之间的间隙,但不包括记录本身。唯一目的就是防止其他事务在这个间隙中插入新记录,解决幻读问题。
它是一个开区间。例如:假设表中有 id为 5, 10, 15 的记录。那么间隙锁可以锁住 (-∞, 5),(5, 10),(10, 15),(15, +∞)这些范围。

触发时机

(1)在可重复读或串行化隔离级别下。
(2)唯一索引/普通索引,等值查询,记录不存在时。

示例

表 user,其 age字段上有普通索引

事务A:
BEGIN;
SELECT * FROM user WHERE age = 25 FOR UPDATE; 
由于 age=25不存在,InnoDB 会找到 25所在的间隙,即 (20, 30)。此时,事务 A 会在 (20, 30)这个间隙上加间隙锁。
事务B:
INSERT INTO user (id, age) VALUES (10, 22); -- 阻塞。因为 22 在 (20, 30) 区间内
INSERT INTO user (id, age) VALUES (10, 28); -- 阻塞。因为 28 在 (20, 30) 区间内
INSERT INTO user (id, age) VALUES (10, 20); -- 成功。20 是已存在的记录,不在间隙锁范围内(但可能被记录锁或临键锁影响)
INSERT INTO user (id, age) VALUES (10, 30); -- 成功。30 是已存在的记录,不在间隙锁范围内
事务 B 的插入操作会被阻塞,直到事务 A 提交。这样就防止了在事务 A 中如果再次执行 SELECT ... FOR UPDATE可能会看到新插入的 age=22或 28的记录(幻读)。

3.临键锁(Next-Key Lock)

行锁 + 间隙锁。它锁住一条记录和该记录之前的间隙。它是一个左开右闭区间。临键锁是 InnoDB 默认的行锁模式
例如:对于记录 id=10,它的临键锁会锁住 (5, 10]这个区间。这意味着既不能插入 id=6的新记录(间隙被锁),也不能修改或删除 id=10的记录(记录被锁)。

示例

(1)等值查询

事务A
SELECT * FROM users WHERE age = 20 FOR UPDATE;

过程:InnoDB 通过 B+ 树找到 age=20 的记录,并给它加上临键锁。

锁定的范围:(10, 20]。

为什么不是 [20, 20]?因为临键锁的本质是锁“下一个键”之前的间隙。对于 age=20,它会锁住 (上一个记录的值, 20],即 (10, 20]。

(2)范围查询

事务A
SELECT * FROM users WHERE age BETWEEN 15 AND 25 FOR UPDATE;

找到第一条满足条件的记录,即 age=20。锁住 (10, 20]。

继续向后扫描,找到 age=30。虽然 30 不满足条件(30 > 25),但根据规则,扫描到的记录也要加锁。锁住 (20, 30]。

最终锁定的范围:(10, 20] 和 (20, 30]。这相当于锁定了整个 (10, 30] 的区间。

效果:任何试图在 age=15 到 age=30 之间插入、更新记录的操作都会被阻塞。

(3)特殊场景:唯一索引等值查询

当使用唯一索引进行等值查询并且数据存在时,InnoDB 会优化,降级为仅使用行锁。

SELECT * FROM users WHERE id = 3 FOR UPDATE;
此时,InnoDB 知道 id=3 是唯一的,不需要防止幻读,所以只会锁住 id=3 这一条记录本身,而不会加间隙锁。

相关SQL语句

-- 查看当前锁信息
SELECT * FROM information_schema.INNODB_LOCKS;
SELECT * FROM information_schema.INNODB_LOCK_WAITS;
-- MySQL 8.0+ 性能schema锁监控
SELECT * FROM performance_schema.data_locks;
SELECT * FROM performance_schema.data_lock_waits;

到此这篇关于MySQL行锁、间隙锁和临键锁示例详解的文章就介绍到这了,更多相关mysql行锁,间隙锁和临键锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • winxp 安装MYSQL 出现Error 1045 access denied 的解决方法

    winxp 安装MYSQL 出现Error 1045 access denied 的解决方法

    自己遇到了这个问题,也找了很久才解决,就整理一下,希望对大家有帮助!
    2010-07-07
  • PHP中常用的几个 mysql操作

    PHP中常用的几个 mysql操作

    本篇文章是对关于php操作mysql执行数据库查询的一些常用操作进行了详细的汇总介绍,非常的细致全面,也很简单,需要的朋友参考下
    2015-04-04
  • 如何在SQL Server中实现 Limit m,n 的功能

    如何在SQL Server中实现 Limit m,n 的功能

    本篇文章是对在SQL Server中实现 Limit m,n功能的方法进行了详细的分析介绍,需要的朋友参考下
    2013-06-06
  • xampp中修改mysql默认空密码(root密码)的方法分享

    xampp中修改mysql默认空密码(root密码)的方法分享

    以前开发我一直都是用的phpnow做php开发环境,phpnow的特点就是一键安装,安装的时候会要求用户输入mysql的root密码。今天由于客户机器使用的xampp作为开发环境,所以碰到了修改mysql默认空密码的问题
    2014-04-04
  • mysql中截取字符串的6个函数讲解

    mysql中截取字符串的6个函数讲解

    这篇文章主要介绍了mysql中截取字符串的6个函数讲解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08
  • MySQL忽略表名大小写的2种方法实现

    MySQL忽略表名大小写的2种方法实现

    在 MySQL 中,默认情况下表名是大小写敏感的,本文主要介绍了MySQL忽略表名大小写的2种方法实现,具有一定的参考价值,感兴趣的可以了解一下
    2024-03-03
  • mysql查询锁表的实现方法

    mysql查询锁表的实现方法

    本文主要介绍了mysql查询锁表的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-09-09
  • 如何解决docker无法启动的问题

    如何解决docker无法启动的问题

    这篇文章主要介绍了如何解决docker无法启动的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-09-09
  • SQL函数实现Group_concat用法

    SQL函数实现Group_concat用法

    GROUP_CONCAT用于将每个分组内的值连接成一个字符串,并通过指定的分隔符进行分隔,本文主要介绍了SQL函数实现Group_concat用法,具有一定的参考价值,感兴趣的可以了解一下
    2024-08-08
  • mysql 8.0.15 winx64解压版图文安装教程

    mysql 8.0.15 winx64解压版图文安装教程

    这篇文章主要为大家详细介绍了mysql 8.0.15 winx64解压版图文安装教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-05-05

最新评论