MySQL FOR SHARE 怎样在一致性读中使用共享锁
当业务需要“先确认一行记录,再在同一个事务里继续处理”时,普通 SELECT 只解决读取问题,并不阻止别的事务修改这行。InnoDB 可以用 SELECT ... FOR SHARE 做锁定读:读取目标行并加共享锁,其他事务仍可读取,但在当前事务提交前不能修改这些行。
官方地址:https://dev.mysql.com/doc/refman/8.4/en/innodb-locking-reads.html
先记住一个判断:只想得到一个一致的快照,用普通 SELECT;需要把“检查结果”保护到后续业务动作,用 FOR SHARE。锁只在事务边界内有意义,不能把它当成查询末尾的装饰语句。
FOR SHARE 解决的不是“读不到”,而是“读完被改了”
普通一致性读会基于 InnoDB 的多版本机制读取某个时间点的快照,不主动给行加锁。它适合报表、详情页和不准备马上修改的数据读取。问题出现在“检查库存、校验状态、读取配置后还要继续处理”的场景:另一个事务可能在检查完成后修改同一行。
FOR SHARE 是锁定读。它读取目标行,并为这些行建立共享锁;其他会话可以继续读取,但不能修改或删除这些行,直到持锁事务提交或回滚。若目标行刚被另一个事务修改但尚未提交,当前锁定读会等待,并在对方事务结束后读取最新值。

在同一个事务中写出最小可用语句
下面用账户表模拟“先读取状态,再决定是否继续”的场景。关键点有两个:表使用 InnoDB,并且锁定读与后续动作处于同一个事务中。
-- 建立一个使用 InnoDB 的最小示例表
CREATE TABLE account_demo (
id BIGINT PRIMARY KEY,
status VARCHAR(16) NOT NULL,
balance DECIMAL(12, 2) NOT NULL
) ENGINE = InnoDB;
-- 共享锁必须和后续业务判断处于同一个事务
START TRANSACTION;
-- 读取账户当前状态,并阻止其他事务修改该行
SELECT id, status, balance
FROM account_demo
WHERE id = 1001
FOR SHARE;
-- 这里根据读取到的状态执行只读检查或后续业务动作
-- 所有动作完成后释放共享锁
COMMIT;
如果只是把 FOR SHARE 放在一个自动提交的单条语句后面,语句结束时事务也可能随之结束,锁的保护范围就很短。需要跨越多条语句保护判断结果时,显式使用 START TRANSACTION、COMMIT 或 ROLLBACK。
普通 SELECT 和 FOR SHARE 的边界怎么选
两种写法都能返回数据,但它们表达的是不同的并发意图。普通 SELECT 关注“我看到哪个快照”,FOR SHARE 关注“我在后续处理前不允许别人改这几行”。不要因为查询看起来只是读取,就默认两者等价。
| 写法 | 读取语义 | 是否阻止并发修改 | 适用场景 |
|---|---|---|---|
SELECT ... | 普通一致性读,读取快照 | 否 | 展示、报表、无需保护的查询 |
SELECT ... FOR SHARE | 锁定读,读取并加共享锁 | 当前事务提交前阻止修改 | 检查后继续处理、保护状态判断 |
SELECT ... FOR SHARE NOWAIT | 尝试立即获得共享锁 | 冲突时立即报错 | 接口不愿意等待的快速失败场景 |
SELECT ... FOR SHARE SKIP LOCKED | 跳过已经被锁住的行 | 不等待冲突行 | 允许换取其他可处理记录的队列读取 |
FOR SHARE 是旧式 LOCK IN SHARE MODE 的替代语法,后者仍可用于兼容旧代码;新代码优先使用 FOR SHARE,因为它支持 OF table_name、NOWAIT 和 SKIP LOCKED 选项。

遇到并发写入时,等待行为来自哪里
可以用两个会话理解等待边界。会话 A 先修改但不提交,会话 B 再执行 FOR SHARE。B 不是“读不到数据”,而是在等待 A 对同一行的未提交修改结束;A 提交后,B 才继续读取最新值。
-- 会话 A:持有未提交的排他修改 START TRANSACTION; UPDATE account_demo SET status = 'locked' WHERE id = 1001; -- 暂不 COMMIT,让其他会话观察锁等待 -- 会话 B:锁定读取同一行,默认会等待会话 A 结束 START TRANSACTION; SELECT id, status, balance FROM account_demo WHERE id = 1001 FOR SHARE; -- 会话 A 提交或回滚后,会话 B 才能继续 COMMIT;
在接口请求不适合长时间等待时,可以使用 NOWAIT。它把锁冲突转成即时错误,应用层可以返回“稍后重试”或选择降级。批量领取任务时,如果允许跳过繁忙记录,则使用 SKIP LOCKED,但必须接受结果集可能暂时不完整。
-- 冲突时立即失败,避免请求线程长时间等待 SELECT id, status FROM account_demo WHERE status = 'ready' FOR SHARE NOWAIT; -- 允许跳过已被其他事务锁住的行,适合可换取下一条记录的场景 SELECT id, status FROM account_demo WHERE status = 'ready' ORDER BY id LIMIT 10 FOR SHARE SKIP LOCKED;
使用前检查四个容易忽略的条件
- 确认存储引擎。
FOR SHARE的事务锁语义针对 InnoDB;不要只看表名就假定所有引擎行为相同。 - 确认事务生命周期。连接池复用时,必须确保成功路径、异常路径都提交或回滚,避免把锁带到下一次请求。
- 确认查询范围。给过滤条件和排序条件准备合适索引,缩小锁定读需要访问的范围,减少等待和死锁机会。
- 确认权限与降级语义。
FOR SHARE需要SELECT权限;NOWAIT与SKIP LOCKED不是“更快的普通查询”,它们分别改变冲突和结果集的处理方式。
排查时可以先确认当前事务隔离级别,再用两个会话复现是否等待。不要在业务高峰临时扩大事务范围,也不要用 SKIP LOCKED 掩盖本应处理的冲突;它适合明确允许跳过的任务分配场景。
常见问题
FOR SHARE 会阻止其他事务读取吗?
不会。共享锁允许其他会话读取,但不允许它们在当前事务提交前修改或删除被锁定的行。具体结果仍受事务隔离级别、访问条件和存储引擎影响。
为什么执行 FOR SHARE 仍然读到了旧值?
先确认使用的是锁定读语法,而不是普通 SELECT,再确认是否存在尚未结束的并发事务。普通一致性读读取快照;锁定读遇到未提交修改时会等待,并在对方事务结束后读取最新值。
FOR SHARE 和 LOCK IN SHARE MODE 要怎么选?
两者是等价语义,LOCK IN SHARE MODE 主要用于兼容旧代码。新代码使用 FOR SHARE,因为表达更清晰,也能配合 OF、NOWAIT 和 SKIP LOCKED。
结论可以收缩成一句话:普通 SELECT 解决一致性读取,FOR SHARE 解决读取后继续处理时的共享锁保护。把它放进明确的事务边界,再根据是否允许等待选择默认行为、NOWAIT 或 SKIP LOCKED,才能让并发语义与业务目标一致。
MySQL DENSE_RANK 怎样保留并列排名记录
- 上一篇
- MySQL DENSE_RANK 怎样保留并列排名记录
- 下一篇
- MySQL optimizer_switch derived_merge 怎样临时关闭派生表合并
-
- 数据库 · MySQL | 51分钟前 |
- MySQL SET_VAR hint 怎样为单条查询临时调整排序内存
- 257浏览 收藏
-
- 数据库 · MySQL | 51分钟前 | MySQL · utf8mb4 ·
- MySQL utf8mb4_0900_ai_ci 怎样处理重音字符比较
- 369浏览 收藏
-
- 数据库 · MySQL | 54分钟前 |
- MySQL gap lock 为什么阻塞范围外的插入
- 191浏览 收藏
-
- 数据库 · MySQL | 55分钟前 | MySQL · InnoDB · mysql READ COMMITTED REPEATABLE READ 一致性读 事务快照
- MySQL 一致性读为什么看不到刚提交的数据
- 499浏览 收藏
-
- 数据库 · MySQL | 1小时前 |
- MySQL JSON_UNQUOTE 怎样去掉 JSON 字符串外层引号
- 209浏览 收藏
-
- 数据库 · MySQL | 1小时前 |
- MySQL 直方图统计信息怎样帮助优化器选择计划
- 400浏览 收藏
-
- 数据库 · MySQL | 1小时前 |
- MySQL REGEXP_REPLACE 怎样清理字段中的连续空格
- 308浏览 收藏
-
- 数据库 · MySQL | 1小时前 |
- MySQL optimizer_switch derived_merge 怎样临时关闭派生表合并
- 423浏览 收藏
-
- 数据库 · MySQL | 1小时前 | MySQL · mysql 窗口函数 并列排名 DENSE_RANK
- MySQL DENSE_RANK 怎样保留并列排名记录
- 161浏览 收藏
-
- 数据库 · MySQL | 1小时前 |
- MySQL 隐形索引怎样安全观察删除影响
- 500浏览 收藏
-
- 数据库 · MySQL | 1小时前 |
- MySQL 降序索引怎样配合 ORDER BY 减少排序
- 121浏览 收藏
-
- 数据库 · MySQL | 1小时前 |
- MySQL SHOW WARNINGS 怎样定位数据截断和隐式转换
- 188浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 761次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 758次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 842次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 810次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 640次使用
-
- MySQL JSON_TABLE 展开数组时如何保留缺失字段
- 2026-09-10 501浏览
-
- MySQL 分区表怎么处理跨分区唯一键:分区列约束与建表取舍
- 2026-08-30 501浏览
-
- MySQL权限管理设置全攻略
- 2026-03-29 501浏览
-
- MySQL分片实现方法及常见方案解析
- 2025-06-24 501浏览
-
- MySQL表空间碎片怎么清理?超详细优化教程
- 2025-06-13 501浏览

