MySQL锁机制:从懵逼到入门,我花了三年

news2026/3/16 20:33:22
MySQL锁机制从懵逼到入门我花了三年写在前面这篇文章源于我被死锁折磨的那些日日夜夜。如果你也曾经对着SHOW ENGINE INNODB STATUS发呆或者被间隙锁搞得怀疑人生那这篇文章可能就是为你写的。一、故事的开始一个诡异的阻塞三年前我刚接手一个电商项目。某天凌晨两点报警群炸了“订单创建接口超时”。我睡眼惺忪地打开监控发现数据库有一堆Lock wait。再看慢查询日志有一条 SQL 格外刺眼SELECT*FROMordersWHEREorder_id12345FORUPDATE;“这不就是查个订单吗怎么还能把数据库卡死” 我当时是这么想的。后来才知道order_id 12345这条记录根本不存在。而在 Repeatable Read 隔离级别下这条 SQL 竟然锁住了整个间隙导致所有插入订单的请求都被阻塞。那一刻我意识到我对 MySQL 锁的理解基本为零。二、锁的粒度从粗到细表锁简单粗暴想象一下你家的卫生间。表锁就像是给整个卫生间上锁。你要上厕所必须把整个卫生间的门锁上。别人想洗手不行等着。想刷牙也得等着。MyISAM 引擎就是这么干的。它的优点是实现简单开销小。缺点嘛……并发性能惨不忍睹。-- 表级读锁LOCKTABLESmy_tableREAD;-- 表级写锁LOCKTABLESmy_tableWRITE;行锁精细管理行锁就像是给卫生间的每个设施单独上锁。马桶、洗手池、淋浴间各自独立。你可以用马桶别人可以同时洗手。InnoDB 引擎支持行锁。这是它成为 MySQL 默认引擎的重要原因之一。但有个坑行锁是锁在索引上的这句话我说了无数遍但很多人还是不信。来看个例子-- 假设 id 是主键SELECT*FROMusersWHEREname张三FORUPDATE;如果name字段没有索引InnoDB 会怎么做全表扫描然后锁住每一行这跟表锁有什么区别所以一定要加索引。这不是建议是铁律。三、锁的模式共享与排他共享锁S锁大家一起看共享锁就是读锁。你可以看我也可以看但谁都不能改。SELECT*FROMusersWHEREid1LOCKINSHAREMODE;-- MySQL 8.0SELECT*FROMusersWHEREid1FORSHARE;排他锁X锁这是我的排他锁就是写锁。我拿了这把锁别人既不能看某些情况下也不能改。UPDATEusersSETage25WHEREid1;-- 或者SELECT*FROMusersWHEREid1FORUPDATE;意向锁提前打招呼这个概念很多人不理解。意向锁是表级锁用来告诉别人“我准备对表里的某些行加锁了”。意向共享锁IS我打算给某些行加 S 锁。意向排他锁IX我打算给某些行加 X 锁。有什么用假设你想给整个表加排他锁LOCK TABLES ... WRITE你怎么知道表里有没有行被锁住了总不能一行行检查吧这时候意向锁就派上用场了。只要检查表上有没有 IS 或 IX 锁就知道有没有行被锁住。四、三种锁算法核心难点来了这部分是重头戏也是大多数人包括当年的我最容易混淆的地方。1. 记录锁Record Lock最简单就是锁住某一行。-- 假设 id3 存在SELECT*FROMusersWHEREid3FORUPDATE;这条 SQL 会对id3这一行加排他锁。2. 间隙锁Gap Lock这个就有点意思了。它不锁记录只锁间隙。假设表里有id 1, 3, 5, 7。你执行SELECT*FROMusersWHEREid4FORUPDATE;id4不存在对吧但在 Repeatable Read 隔离级别下InnoDB 会加一个间隙锁锁住(3, 5)这个区间。为什么要这么做防止幻读。如果别人在你查询的间隙里插入了一条id4的记录你再次查询时就会看到这条凭空出现的数据这就是幻读。3. 临键锁Next-Key Lock这个名字听起来很玄乎其实就是记录锁 间隙锁的组合。InnoDB 在 Repeatable Read 级别下默认使用临键锁。它锁住的是(prev, current]这个区间左开右闭。举个例子表里有id 2, 5, 9。你执行SELECT*FROMusersWHEREid5ANDid9FORUPDATE;InnoDB 会怎么加锁对id5加临键锁锁住(2, 5]对id9加临键锁锁住(5, 9]注意不会锁住(9, ∞)因为你的查询条件是 9插入id10不会影响你的查询结果。一个重要的优化很多人不知道InnoDB 有个聪明的优化如果是唯一索引的等值查询且记录存在临键锁会退化成记录锁。-- id 是主键唯一索引SELECT*FROMusersWHEREid5FORUPDATE;-- id5 存在这条 SQL 只会对id5加记录锁不会加间隙锁。为什么因为id是唯一的不可能有另一条id5的记录插入。既然不会有幻读为什么要锁间隙这个优化极大地提高了并发性能。五、隔离级别锁的行为大不同MySQL 有四种隔离级别但常用的只有两种Read Committed (RC)和Repeatable Read (RR)。RR默认安全但保守RR 是 MySQL 的默认隔离级别。它的特点是使用临键锁防止幻读范围查询会锁住间隙并发性能相对较低RC激进但高效RC 隔离级别下不加间隙锁这是关键只锁住实际存在的记录并发性能更高这意味着什么在 RC 级别下你执行SELECT*FROMusersWHEREid4FORUPDATE;-- id4 不存在不会加任何锁因为记录不存在RC 级别不会加间隙锁。再比如SELECT*FROMusersWHEREid5ANDid9FORUPDATE;只会对id5和id9加记录锁不会锁住间隙。别人可以自由插入id6, 7, 8。那幻读怎么办RC 级别允许幻读。这是用一致性换取性能的权衡。我该选哪个金融、支付类系统用 RR数据一致性更重要高并发互联网应用考虑 RC性能更重要当然要能接受幻读我参与过的项目90% 都改成了 RC 级别。原因很简单间隙锁导致的锁等待真的太常见了。六、MVCC锁的好兄弟很多人以为防止脏读、不可重复读、幻读全靠锁。这是错的。InnoDB 有个神器叫MVCC多版本并发控制。它的作用是普通 SELECT快照读不加锁通过 MVCC 读取历史版本防止脏读只能看到已提交的数据防止不可重复读RR 级别整个事务看到同一个快照关键区别-- 快照读不加锁用 MVCCSELECT*FROMusersWHEREid1;-- 当前读加锁读最新版本SELECT*FROMusersWHEREid1FORUPDATE;UPDATEusersSETage25WHEREid1;DELETEFROMusersWHEREid1;所以防止幻读的机制有两个快照读靠 MVCCRR 级别下整个事务看到同一个快照自然不会有幻读当前读靠间隙锁/临键锁防止别人插入新数据七、实战如何排查锁问题理论说了这么多来点实际的。1. 查看当前锁-- MySQL 8.0SELECT*FROMperformance_schema.data_locks;-- 查看锁等待SELECT*FROMperformance_schema.data_lock_waits;2. 查看死锁信息SHOWENGINEINNODBSTATUS;在输出结果里找LATEST DETECTED DEADLOCK部分。这里会详细告诉你哪个事务持有什么锁哪个事务在等待什么锁死锁是怎么形成的3. 一个真实的死锁案例事务 ABEGIN;UPDATEusersSETage25WHEREid1;UPDATEusersSETage26WHEREid2;事务 BBEGIN;UPDATEusersSETage27WHEREid2;UPDATEusersSETage28WHEREid1;看到了吗事务 A 锁了id1想锁id2事务 B 锁了id2想锁id1。典型的死锁。解决方案统一访问顺序。所有事务都先访问id1再访问id2。4. 间隙锁导致的阻塞这个更隐蔽。事务 ABEGIN;SELECT*FROMusersWHEREid100FORUPDATE;-- id100 不存在事务 BINSERTINTOusers(id,name)VALUES(100,张三);-- 阻塞为什么因为事务 A 在 RR 级别下对不存在的id100加了间隙锁。事务 B 的插入操作被阻塞。解决方案改用 RC 隔离级别确保查询的记录存在用INSERT ... ON DUPLICATE KEY UPDATE代替SELECT INSERT/UPDATE八、避坑指南我踩过的雷坑 1忘记加索引-- name 字段没有索引SELECT*FROMusersWHEREname张三FORUPDATE;后果全表锁。所有对users表的写操作都会被阻塞。检查方法用EXPLAIN看 SQL 是否走索引。坑 2范围查询锁住大片间隙SELECT*FROMordersWHEREcreated_at2024-01-01FORUPDATE;后果锁住从2024-01-01到正无穷的所有间隙。新订单无法插入。解决方案改用 RC 隔离级别缩小查询范围比如只查某一天的订单避免对范围查询加锁坑 3大事务长时间持有锁BEGIN;-- 处理 10000 条订单UPDATEordersSETstatus1WHEREidIN(...);-- 发送 10000 封邮件-- 调用外部 API-- ... 过了 30 秒COMMIT;后果锁被持有 30 秒其他事务全部阻塞。解决方案大事务拆成小事务批量处理每 100 条提交一次把耗时操作发邮件、调 API放到事务外坑 4RR 级别下批量插入不存在的 ID-- 批量检查订单是否存在SELECTidFROMordersWHEREidIN(1001,1002,1003,...)FORUPDATE;-- 然后插入不存在的订单如果这些 ID 都不存在在 RR 级别下会锁住大片间隙导致并发插入性能极差。解决方案改用 RC用INSERT ... ON DUPLICATE KEY UPDATE先查已存在的 ID只处理不存在的九、最佳实践我现在的做法经过这些年的踩坑我总结了一套自己的实践1. 隔离级别默认 RC除非有强一致性要求否则一律用 RC。间隙锁导致的性能问题真的太常见了。2. 索引必须加所有WHERE、JOIN、ORDER BY的字段都要考虑加索引。用EXPLAIN验证。3. 事务越小越好只包含必要的 SQL耗时操作放到事务外批量操作分批提交4. 锁能不用就不用优先用乐观锁version字段避免SELECT ... FOR UPDATE除非真的需要如果必须用确保走唯一索引5. 监控必须做监控锁等待时间监控死锁次数定期分析慢查询日志十、总结MySQL 的锁机制说复杂也复杂说简单也简单。核心就几点行锁是锁在索引上的—— 没索引 表锁RR 级别有间隙锁RC 级别没有—— 这是性能差异的关键临键锁 记录锁 间隙锁—— RR 级别的默认策略MVCC 负责快照读锁负责当前读—— 两者配合实现隔离唯一索引等值查询会优化—— 临键锁退化成记录锁最后送大家一句话锁是用来保护数据的不是用来折磨开发者的。理解它才能用好它。希望这篇文章能帮你少走点弯路。如果还有疑问欢迎留言讨论。毕竟我也是从懵逼过来的。参考资源MySQL 官方文档InnoDB Locking《高性能 MySQL》第 7 章源码storage/innobase/lock/lock0lock.cc实验环境MySQL 8.0.32隔离级别Repeatable Read除非特别说明引擎InnoDB如果你觉得这篇文章有帮助欢迎转发给更多被锁折磨的朋友。毕竟独惨惨不如众惨惨开玩笑的是共同进步。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2417081.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…