谈谈Select For Update的实现原理?

文章内容收录到个人网站,方便阅读:hardyfish.top/

资料分享

MySQ技术内幕第5版:

SELECT FOR UPDATE 是 SQL 中的一种行锁机制,用于在事务中对查询到的数据行加锁。

它的作用是阻止其他事务对这些行进行修改或获取锁,通常用于需要保证数据一致性的场景。

在 MySQL 中,SELECT FOR UPDATE 的具体实现与存储引擎(如 InnoDB)密切相关,其核心是利用 行锁(Row Lock) 来实现对目标行的加锁操作。

核心特性

  1. 锁的类型

    • SELECT FOR UPDATE 会对查询到的行加 排他锁(Exclusive Lock, X 锁)
    • 其他事务不能修改这些行,也不能对其加共享锁或排他锁。
  2. 适用范围

    • 必须在事务中使用(即 BEGINCOMMIT 之间)。
    • 只有使用支持事务的存储引擎(如 InnoDB)时才有效。
  3. 行为

    • 如果目标行已经被其他事务加锁,则当前事务会进入等待状态,直到锁被释放或超时。

实现原理

  1. InnoDB 的行锁机制
  • InnoDB 存储引擎通过 索引 来实现行级锁定。
  • SELECT FOR UPDATE 会加锁所有满足查询条件的行。如果查询没有使用索引,会退化为 表锁,即锁定整个表。
  • 加锁过程
  • 查询执行时,InnoDB 会为扫描到的每一行尝试加排他锁。
  • 如果某一行已经被其他事务锁定,当前事务会等待该锁被释放。
  • 锁类型
  • 行锁:基于索引的锁,仅锁定查询到的行。
  • 间隙锁(Gap Lock):在可重复读(REPEATABLE READ)隔离级别下,如果范围查询未命中行,会对范围间隙加锁,防止插入数据。
  • 事务隔离级别的影响
  • 读提交(READ COMMITTED)

    • 每次查询都会读取最新的数据,只对当前查询结果加锁。
  • 可重复读(REPEATABLE READ)

    • 查询结果基于事务快照,锁定的范围可能包括未命中的行(使用间隙锁)。
  • 串行化(SERIALIZABLE)

    • 整个查询范围内的数据都会被锁定。

执行流程

以下以 InnoDB 为例说明 SELECT FOR UPDATE 的执行流程:

  1. 事务开始

    • 开启事务(BEGIN)。
  2. 查询并加锁

    • 执行 SELECT ... FOR UPDATE
    • 对满足条件的记录加排他锁(X 锁)。
  3. 数据操作

    • 修改或读取加锁的数据。
  4. 释放锁

    • 事务提交(COMMIT)后,释放锁。
    • 如果事务回滚(ROLLBACK),同样释放锁。

示例

基本用法

SQL 复制代码
BEGIN;
SELECT * FROM orders WHERE order_id = 1 FOR UPDATE;
/* 对 order_id=1 的行加锁 */
UPDATE orders SET status = 'processed' WHERE order_id = 1;
COMMIT;

多事务竞争场景

  1. 事务 A:

    SQL 复制代码
    BEGIN;
    SELECT * FROM orders WHERE order_id = 1 FOR UPDATE;
    -- 对 order_id=1 的行加锁
  2. 事务 B(在事务 A 未提交之前):

    SQL 复制代码
    BEGIN;
    SELECT * FROM orders WHERE order_id = 1 FOR UPDATE;
    -- 等待事务 A 释放锁

事务 B 在事务 A 提交或回滚之前无法获得锁。

间隙锁的行为

REPEATABLE READ 隔离级别下,范围查询可能触发间隙锁。例如:

SQL 复制代码
SELECT * FROM orders WHERE order_id BETWEEN 10 AND 20 FOR UPDATE;
  • 如果查询结果为空,InnoDB 会对 1020 的范围加间隙锁,阻止其他事务在此范围内插入新记录。

优化与注意事项

  1. 索引的使用

    • 使用索引的查询会加行锁,避免退化为表锁。
    • 未使用索引时,可能会锁定整个表,影响并发性能。
  2. 避免长事务

    • 锁的持有时间过长会导致其他事务阻塞,应尽量缩短事务的执行时间。
  3. 死锁检测

    • InnoDB 自动检测死锁并选择其中一个事务回滚。建议合理设计事务逻辑以避免死锁。
  4. 结合业务需求使用

    • 仅在确实需要防止数据修改冲突时使用 SELECT FOR UPDATE,避免不必要的锁竞争。

总结

  1. SELECT FOR UPDATE 是 MySQL 中通过行锁机制实现的一种锁定操作,主要用于防止并发修改冲突。
  2. 其实现依赖于事务、隔离级别和索引优化。
  3. 合理使用 SELECT FOR UPDATE 可以有效保护数据一致性,但需要注意性能开销,避免锁竞争和死锁问题。
相关推荐
NineData2 小时前
NineData 迁移评估功能正式上线
数据库·dba
哈里谢顿3 小时前
1000台裸金属并发创建中的重难点问题分析
面试
哈里谢顿3 小时前
20260303面试总结(全栈)
面试
橙序员小站4 小时前
Agent Skill 是什么?一文讲透 Agent Skill 的设计与实现
前端·后端
怒放吧德德4 小时前
Netty 4.2 入门指南:从概念到第一个程序
java·后端·netty
雨中飘荡的记忆6 小时前
大流量下库存扣减的数据库瓶颈:Redis分片缓存解决方案
java·redis·后端
NineData7 小时前
数据库迁移总踩坑?用 NineData 迁移评估,提前识别所有兼容性风险
数据库·程序员·云计算
开心就好20257 小时前
UniApp开发应用多平台上架全流程:H5小程序iOS和Android
后端·ios
悟空码字7 小时前
告别“屎山代码”:AI 代码整洁器让老项目重获新生
后端·aigc·ai编程
小码哥_常7 小时前
大厂不宠@Transactional,背后藏着啥秘密?
后端