news 2026/6/12 2:33:14

MySQL如何实现S锁?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MySQL如何实现S锁?

它的本质是:**S 锁不是一把“禁止进入”的锁,而是一张“允许共存”的通行证

  • 核心定义
    • S 锁 (Shared Lock):又称读锁。当事务对数据行加上 S 锁后,其他事务也可以对该行加 S 锁,但不能加 X 锁(排他锁/写锁)。
    • X 锁 (Exclusive Lock):又称写锁。当事务对数据行加上 X 锁后,其他事务既不能加 S 锁,也不能加 X 锁。
  • 存在理由
    1. 支持并发读:多个用户可以同时读取同一份数据,互不干扰。这是数据库高并发读的基石。
    2. 保证一致性读:在需要强一致性的场景下(如金融转账前的余额查询),防止在读的过程中数据被修改。
    3. 与 MVCC 互补:普通SELECT使用 MVCC(快照读,不加锁);SELECT ... LOCK IN SHARE MODE使用 S 锁(当前读,加锁)。
  • 核心逻辑别把 S 锁当成“只读保护”。把它当成读者俱乐部的会员卡。持有会员卡的人可以一起看书(并发读),但如果有人想撕书或改书(加 X 锁),必须等所有持卡人都离开后才能进行。

如果把数据行比作图书馆的一本书

  • 无锁:谁都可以拿,容易抢起来。
  • S 锁 (共享锁)
    • 张三借走了书(加了 S 锁)。
    • 李四也想看,也可以借走(再加一个 S 锁)。
    • 王五也想看,也可以借走(再加一个 S 锁)。
    • 赵六想修改书里的内容(加 X 锁):管理员说:“不行,现在还有三个人在看呢,等他们还回来再说。” ->阻塞
  • X 锁 (排他锁)
    • 张三借走书并说要修改(加了 X 锁)。
    • 李四想看?不行,等着。
    • 王五想改?不行,等着。
    • 核心逻辑S 锁的核心价值在于读读兼容 (Read-Read Compatible),但读写互斥 (Read-Write Exclusive)

一、底层实现机制:锁在哪里?

1. 锁存储在索引上
  • 事实:和行锁一样,S 锁也是加在索引记录 (Index Record)上的。
  • 机制
    • InnoDB 在 B+ 树的叶子节点中,为每个索引记录维护一个锁列表 (Lock List)
    • 锁列表中包含持有该锁的事务 ID 和锁类型(S 或 X)。
    • 当新事务请求 S 锁时,InnoDB 遍历锁列表:
      • 如果列表中只有 S 锁:批准,加入列表。
      • 如果列表中有 X 锁:拒绝,进入等待队列。
2. 意向锁 (Intention Locks) —— 表级的快速检查
  • 问题:如果我要给整张表加锁,难道要检查每一行的锁吗?太慢了。
  • 解决方案:意向锁。
    • IS (Intention Shared):事务打算在某行加 S 锁前,先在表级别加 IS 锁。
    • IX (Intention Exclusive):事务打算在某行加 X 锁前,先在表级别加 IX 锁。
  • 作用
    • 如果我想给全表加 S 锁,我只需检查表上是否有 IX 锁。如果有,说明有人要在某行写,全表 S 锁失败。
    • 价值:将表锁的检查复杂度从 O(N) 降为 O(1)。

💡 核心洞察S 锁是细粒度的(行级),但通过意向锁实现了粗粒度(表级)的快速冲突检测。


二、兼容性矩阵:谁能和谁共存?

这是理解 S 锁的关键。

当前锁 \ 请求锁S (共享)X (排他)
S (共享)✅ 兼容❌ 冲突
X (排他)❌ 冲突❌ 冲突
  • S + S = OK:大家都能读。
  • S + X = Wait:有人在读,写的必须等。
  • X + S = Wait:有人在写,读的必须等。
  • X + X = Wait:有人在写,另一个写的也必须等。

💡 核心洞察S 锁的唯一天敌是 X 锁。只要没有写操作,S 锁可以无限叠加。


三、获取方式:PHP 中如何触发 S 锁?

在 MySQL 中,有两种主要方式获取 S 锁:

1. 显式锁定读 (Explicit Locking Read)
  • SQLSELECT ... LOCK IN SHARE MODE;(MySQL 8.0+ 推荐SELECT ... FOR SHARE)
  • 场景
    • 你需要读取一行数据,并确保在你处理完业务逻辑并提交之前,其他人不能修改它
    • 例如:检查父记录是否存在,然后插入子记录。
  • PHP 代码
    $pdo->beginTransaction();// 加 S 锁$stmt=$pdo->query("SELECT * FROM categories WHERE id = 1 FOR SHARE");$category=$stmt->fetch();if($category){// 此时其他事务可以读 category,但不能修改它// 直到本事务 commit$pdo->exec("INSERT INTO products (cat_id, name) VALUES (1, 'Book')");}$pdo->commit();
2. 外键约束检查 (Foreign Key Checks)
  • 机制:当你在子表中插入或更新外键字段时,InnoDB 会自动在父表的对应记录上加S 锁
  • 目的:确保父记录在子记录引用期间不会被删除或修改主键。
  • 价值:自动维护参照完整性,无需手动加锁。

四、S 锁 vs. MVCC:为什么平时 SELECT 不加锁?

这是初学者最大的困惑。

1. 快照读 (Snapshot Read)
  • 语句:普通SELECT * FROM t;
  • 机制:不加任何锁。通过Undo LogRead View读取历史版本。
  • 价值:极高的并发读性能。读写完全互不阻塞。
  • 缺点:读到的是旧数据(不可重复读或幻读,取决于隔离级别)。
2. 当前读 (Current Read)
  • 语句SELECT ... FOR SHARE,SELECT ... FOR UPDATE,UPDATE,DELETE.
  • 机制:加锁(S 或 X)。读取最新数据。
  • 价值:保证数据的新鲜性和一致性。
  • 缺点:有锁竞争,并发度低于快照读。

💡 核心洞察MySQL 默认使用 MVCC 来实现“读不加锁”,只有在显式要求或写操作时才使用 S/X 锁。这是 MySQL 高并发的秘密武器。


五、认知牢笼:常见误区

1. 误区:“S 锁会阻塞其他 S 锁。”
  • 真相
    • 不会。S 锁之间是完全兼容的。
    • 对策:放心让多个用户同时执行FOR SHARE
2. 误区:“普通的 SELECT 也会加 S 锁。”
  • 真相
    • 不会。普通 SELECT 是快照读,不加锁。
    • 对策:只有FOR SHARE/LOCK IN SHARE MODE才加 S 锁。
3. 误区:“S 锁能防止幻读。”
  • 真相
    • 单独 S 锁不能防止幻读。需要配合Next-Key Lock(S 锁 + Gap Lock) 才能在 RR 级别下防止幻读。
    • 对策:理解 S 锁只是记录锁,范围查询时会自动升级为 Next-Key Lock。
4. 误区:“S 锁性能很差。”
  • 真相
    • 相比 X 锁,S 锁的并发度高得多。
    • 相比 MVCC,S 锁有开销。
    • 对策:仅在需要强一致性读时使用FOR SHARE,否则用普通SELECT
5. 误区:“S 锁只在 RR 级别有效。”
  • 真相
    • S 锁在所有隔离级别下都有效。
    • 但在 RC 级别下,S 锁的范围可能不同(无 Gap Lock)。
    • 对策:根据业务需求选择隔离级别。

🚀 总结:原子化“MySQL S 锁”全景图

维度关键点
本质允许多个事务同时读取同一资源的共享机制
核心特性读读兼容,读写互斥
实现位置索引记录 + 意向锁 (表级)
获取方式SELECT ... FOR SHARE, 外键检查
与 MVCC 关系MVCC 用于快照读(无锁),S 锁用于当前读(有锁)
PHP 隐喻Library Reading Room (S Lock) vs. Editing Desk (X Lock)
公式Concurrency = (Shared_Access × Compatibility) ^ Write_Exclusion

终极心法

S 锁的本质,是“对共享的优雅管理”。
它让阅读变得自由,让写入变得有序。
它是并发世界中温柔的一面。
于兼容中见效率,于互斥中见秩序;以共享为尺,解独占之牛,于数据交互中,求和谐之真。

行动指令

  1. 实验兼容性:开启两个事务。事务 A 执行SELECT ... FOR SHARE。事务 B 也执行SELECT ... FOR SHARE。观察是否阻塞(不应阻塞)。
  2. 测试互斥:事务 A 执行SELECT ... FOR SHARE。事务 B 执行UPDATE。观察事务 B 是否阻塞(应阻塞)。
  3. 对比 MVCC:对比普通SELECTSELECT ... FOR SHARE在并发下的表现差异。
  4. 思维升级:记住,S 锁是强一致性读的利器,但 MVCC 是高并发读的基石。明智地选择两者,才能平衡一致性与性能。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/12 2:27:53

别小看这颗并联的小电容:前馈电容如何让你的模块电源‘快准稳’?

别小看这颗并联的小电容:前馈电容如何让你的模块电源‘快准稳’?在开源硬件项目中,电源模块的稳定性常常是决定成败的关键细节。想象一下,当你精心设计的Arduino机器人突然启动电机时,系统电压像过山车一样剧烈波动——…

作者头像 李华
网站建设 2026/6/12 2:21:51

企业微信SCRM哪个好?2026年企业微信客户管理工具服务商选型测评与金融汽车零售等行业实战指导

企业微信SCRM选错了会怎样?系统买了用不起来、数据对接不上、服务商找不到人……这些问题在中大型企业里并不少见。2026年选企业微信服务商,核心看六大硬指标:合规安全、AI能力、功能完整性、系统集成、易用性、服务性价比。这篇文章讲清楚三…

作者头像 李华
网站建设 2026/6/12 2:19:52

Spark Streaming直连Kafka:从‘能用’到‘好用’的性能调优与监控实战

Spark Streaming直连Kafka:从‘能用’到‘好用’的性能调优与监控实战当实时数据流水线从测试环境走向生产环境时,许多开发者会发现原本平稳运行的Spark Streaming应用开始暴露出各种性能问题。数据量激增带来的消费延迟、Executor内存溢出或任务堆积&am…

作者头像 李华
网站建设 2026/6/12 2:18:52

原生插件什么时候直接返回结果,什么时候改为事件回调

适合谁看 正在设计 Flutter 与鸿蒙原生插件接口的人 不确定某个能力该走结果返回还是事件回推的人 想减少后续返工的人 问题背景 很多平台能力在刚开始接入时,看起来都像: Flutter 发一个方法 原生回一个结果 这会让人很容易产生一种错觉&#x…

作者头像 李华
网站建设 2026/6/12 2:17:53

3分钟终极指南:用DS4Windows让PlayStation手柄在PC上完美运行

3分钟终极指南:用DS4Windows让PlayStation手柄在PC上完美运行 【免费下载链接】DS4Windows Like those other ds4tools, but sexier 项目地址: https://gitcode.com/gh_mirrors/ds/DS4Windows 你是否遇到过这样的尴尬场景:在Steam上购买了心仪的游…

作者头像 李华