谈谈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. 适用范围:

    • 必须在事务中使用(即 BEGIN 和 COMMIT 之间)。
    • 只有使用支持事务的存储引擎(如 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 会对 10 到 20 的范围加间隙锁,阻止其他事务在此范围内插入新记录。

优化与注意事项

  1. 索引的使用:

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

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

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

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

总结

  1. SELECT FOR UPDATE 是 MySQL 中通过行锁机制实现的一种锁定操作,主要用于防止并发修改冲突。
  2. 其实现依赖于事务、隔离级别和索引优化。
  3. 合理使用 SELECT FOR UPDATE 可以有效保护数据一致性,但需要注意性能开销,避免锁竞争和死锁问题。
相关推荐
系统集成-鹰翔6 小时前
ERP对接升级提示数据库迁移失败:SQLite逐版本事务到底回滚到哪一步
javascript·数据库·经验分享·后端
西瓜拿铁好喝6 小时前
开源和闭源 AI 办公工具,分别适合谁
java·前端·数据库
IT_陈寒6 小时前
深入理解SpringBoot:核心原理与最佳实践
前端·人工智能·后端
学长毕设6 小时前
基于SpringBoot的校园礼服租赁系统的设计与实现
java·spring boot·后端
代码派6 小时前
业务不停机迁移 MySQL 到 TiDB,NineData 数据增量同步实操演示
数据库·mysql·tidb·ninedata·无停机迁移·增量迁移·全量迁移
Gauss松鼠会6 小时前
GaussDB 中 gsql 与 DBeaver(JDBC)客户端工具查询时间不一致:原因、排查与处理
运维·服务器·数据库·sql·gaussdb·经验总结
Elastic 中国社区官方博客6 小时前
基于 Jina OCR v1 与 Elasticsearch 的 PDF 智能混合检索与 RAG 系统实战指南
大数据·数据库·elasticsearch·搜索引擎·ocr·jina
Best_Liu~6 小时前
SpringBoot全局日期格式化
java·spring boot·后端
小蒜学长6 小时前
基于Django+Vue喵汪联盟宠物领养系统的设计与实现(代码+数据库+LW)
后端·python·django·宠物领养系统·流程规范化
楚识科技6 小时前
信创环境下 OCR 部署实践:从麒麟系统到达梦数据库的全栈适配
数据库·ocr