【MySQL百日打怪升级第20天】行锁 vs 表锁 —— InnoDB 什么时候锁行、什么时候锁表?

简介: 【第20天】MySQL锁机制精讲!深入解析InnoDB行锁与表锁的本质区别:行锁依赖索引,无索引则全表扫描→实际锁全表;详解Record/Gap/Next-Key三种行锁、意向锁作用及锁诊断实战(PS视图+INNODB STATUS)。避坑指南:UPDATE/DELETE务必走索引!

【第20天】每天一个MySQL知识点,百日打怪升级

行锁 vs 表锁 —— InnoDB 什么时候锁行、什么时候锁表?


大家好,我是一名拥有10年以上经验的DBA老兵,没有那多。

做这个系列,源于一个朴素的愿望:把踩过的坑、总结的经验系统化输出,希望能帮到刚入行或想进阶的兄弟们。

让我们开始今天的第20天内容。


前三天聊了事务隔离级别和三大问题。但有个东西一直藏在背后——锁。

隔离级别不是概念,是靠 MVCC(多版本并发控制)和锁配合实现的。RC怎么做到避免脏读?RR怎么做到的不可重复读?靠的是MVCC一致性读快照。Serializable靠什么串行化?靠的是给普通SELECT也隐式加锁。而MVCC的底层,也离不开锁来保护数据结构的并发访问。

但大多数人第一次接触到"锁"这个字,不是从原理书里,而是从一条报警短信里:

Lock wait timeout exceeded; try restarting transaction

然后就开始百度"MySQL锁等待超时怎么办"。今天咱们把这个东西讲透——从最基础的行锁 vs 表锁开始。


最核心的区别:锁住了什么

锁类型 锁的范围 并发度 典型场景
行锁 只锁住命中的那几行 高(其他人可以操作别的行) InnoDB 默认,有索引时
表锁 锁住整张表 极低(别人啥都干不了) 手动 LOCK TABLES、MyISAM 表、DDL 元数据锁

行锁:你在表上有一行 id=1 的数据加了行锁,别人照样可以改 id=2。互不影响。

表锁:你锁了整张表,别人想改任何一行都得等你释放。

听起来行锁完胜对不对?但事情没这么简单。


致命陷阱:看似行锁,实际是表锁

这是生产环境最常见的锁故障,没有之一。

InnoDB 的行锁是挂在索引上的。如果你的 WHERE 条件没有命中索引,InnoDB 只能全表扫描——会锁住扫描过程中碰到的每一行。最终结果是整张表的所有行都被锁住了,并发效果和表锁完全一样。(注意这不算真正的"锁升级",InnoDB 没有行锁→表锁的升级机制,本质是一大堆行锁。)

-- 假设表结构:CREATE TABLE t1 (id INT PRIMARY KEY, name VARCHAR(50), age INT);
-- 索引情况:id 有主键索引,age 没有索引

-- 事务 A:按非索引列更新
BEGIN;
UPDATE t1 SET name = 'new' WHERE age = 25;
-- age 没有索引,InnoDB 只能全表扫描
-- 每一行都会加锁,最终所有行都被锁住!

-- 事务 B:想更新另一行(完全不同的数据)
UPDATE t1 SET name = 'other' WHERE id = 2;
-- 被阻塞!明明操作的不是同一行

这就是经典的生产事故:你在开发环境跑得好好的,因为数据量小,全表扫描很快。上了生产,某个查询走了全表扫描,把所有行都锁死了,所有更新排队,系统瞬间卡死。

我处理过一个case:凌晨2点告警,某核心表的更新全部超时。查出来是一个定时任务跑了 UPDATE ... WHERE status = 'pending',status 字段没有索引,60万行全表扫描,锁了10分钟。加了个索引,问题消失。

原则:UPDATE 和 DELETE 的 WHERE 条件一定要走索引。不是为了快,是为了避免锁住全表。


InnoDB 的三种行锁

锁不是非黑即白的。InnoDB 在行级别上有三种锁模式:

锁类型 别名 作用
Record Lock 行锁 只锁住索引上的某一条记录
Gap Lock 间隙锁 锁住索引记录之间的空隙,防止插入
Next-Key Lock 临键锁 Record Lock + Gap Lock 的组合

Next-Key Lock 是最有意思的。它锁的不是"一个值",而是"一个区间"。比如你的索引值有 5、10、15,Next-Key Lock 会锁住 (-∞, 5](5, 10](10, 15](15, +∞) 这些区间。

这就是 RR 级别下防幻读的秘密:你查 WHERE id > 5 AND id < 15 FOR UPDATE,InnoDB 把 (5, 10](10, 15) 区间都锁住了,别人想插入 id=8 或 id=12——等你的事务结束再说。

不过话说回来,有一次我调试一个奇怪的锁等待问题,查 SHOW ENGINE INNODB STATUS 看了半天没看出名堂。后来发现是我把 RC 和 RR 搞混了——RC 下 Gap Lock 是关闭的,我以为加了 FOR UPDATE 就能防幻读,实际上在 RC 下 FOR UPDATE 只锁行不锁间隙。吃了一次亏之后长了记性。

RR 下:SELECT ... FOR UPDATE → Next-Key Lock(行+间隙一起锁)
RC 下:SELECT ... FOR UPDATE → Record Lock(只锁行,不锁间隙)

意向锁:你感觉不到但一直在用的锁

InnoDB 还有两种"幕后锁":意向共享锁(IS)意向排他锁(IX)

你不用手动加它们,InnoDB 自动管理。它们的作用是——告诉你"有人正在锁某行"

场景:你想给整张表加表锁,但是有很多行已经被别的事务锁住了。如果没有意向锁,你得遍历整张表检查每一行。有了意向锁,你只需要检查"表级别上有没有 IX 锁"——有,说明有人在锁行,你等。

意向锁的存在是为了行锁和表锁能共存。它是表级别的锁,由 lock manager 统一管理。你可以把它理解为"我打算在行上做点什么"的提前声明——加 IX 锁表示"我要改某行",别人看到 IX 就知道不能给整张表加排他锁了。


锁的诊断:出了问题怎么看?

线上出现锁等待了,第一件事不是重启,是查"谁在等谁"。

但先说清楚开这个东西的成本:

第一层performance_schema=ON。MySQL 5.7+ 默认就是 ON,不用你操心。但如果你在某个旧实例上发现它是 OFF,想打开的话——my.cnfperformance_schema=ON,然后重启实例,没法动态改。这一步本身已经有内存开销了(几百 MB 左右)。

第二层data_lock_waits 能看到谁在等谁,但要查出持有锁的那条 SQL 是什么,还需要 events_statements_current consumer 也开着。这个 consumer 不开,data_lock_waits 里的 THREAD_ID 就关联不到具体的 SQL 文本,你只知道"某个线程在堵",不知道"它在跑什么 SQL"。

先检查当前状态:

SHOW VARIABLES LIKE 'performance_schema';
SELECT * FROM performance_schema.setup_consumers 
WHERE NAME LIKE 'events_statements%';

如果 events_statements_current 是 NO,可以动态开启(不需要重启):

UPDATE performance_schema.setup_consumers 
SET ENABLED = 'YES' 
WHERE NAME = 'events_statements_current';

注意这个 consumer 会在内存里缓存每个线程最近执行的 SQL,线程越多占的内存越大。如果连接数上千,这个开销就不小了。

首选方案(MySQL 8.0,PS 开启 + events_statements_current 开启):

-- 谁被阻塞了?
SELECT * FROM performance_schema.data_lock_waits;

-- 从 THREAD_ID 找到对应的连接 ID(KILL 需要的是 PROCESSLIST_ID)
SELECT PROCESSLIST_ID 
FROM performance_schema.threads 
WHERE THREAD_ID = <BLOCKING_THREAD_ID>;

-- 看看阻塞者最近在跑什么 SQL
SELECT THREAD_ID, SQL_TEXT 
FROM performance_schema.events_statements_current 
WHERE THREAD_ID = <BLOCKING_THREAD_ID>;

-- 拿到 PROCESSLIST_ID 后,低频任务直接 KILL
-- KILL <PROCESSLIST_ID>;
-- 高频业务先去查代码

兜底方案(PS 关掉了或者不想开 events_statements_current,也能用):

-- 方案一:SHOW PROCESSLIST
-- 看 State 列:如果有 "Waiting for table level lock" 或 "Waiting for row lock"
SHOW PROCESSLIST;

-- 方案二:SHOW ENGINE INNODB STATUS
-- 重点看 "LATEST DETECTED DEADLOCK" 和 "TRANSACTIONS" 章节
-- 内容比较难读,但胜在永远能用
SHOW ENGINE INNODB STATUS\G

-- 方案三(MySQL 5.7):information_schema 旧视图
SELECT * FROM information_schema.INNODB_LOCK_WAITS;
SELECT * FROM information_schema.INNODB_LOCKS;

🤖 AI实战工具箱:让AI帮你造锁测试

锁的问题也是典型的"并发才出现、单机不暴露"。AI 擅长生成多 session 的竞争脚本。

复现行锁升级表锁

直接把下面这段粘给AI:

帮我写一组MySQL测试SQL,演示InnoDB中UPDATE操作在未命中索引时锁住所有行,效果等同于表锁。需要两个终端,终端A按无索引的age字段UPDATE,终端B按主键UPDATE另一行——验证B被阻塞。再建一个索引后重新测试,验证B不再被阻塞。用SLEEP控制时序,加注释说明。

复现死锁

帮我写一组MySQL测试SQL,演示两个事务互相等待的经典死锁场景。事务A锁住行1等待行2,事务B锁住行2等待行1。用SLEEP控制时序,最后用SHOW ENGINE INNODB STATUS查看死锁信息。

分析锁等待

我有一个锁等待超时的报警,下面是我的SHOW ENGINE INNODB STATUS输出,帮我分析是哪条SQL阻塞了哪条SQL:[粘贴 SHOW ENGINE INNODB STATUS 输出]

第三个场景才是最实用的——线上出问题的时候,把锁状态贴给AI,几秒钟就能定位到阻塞链。比肉眼盯着TRANSACTIONS章节看快多了。


面试加餐

Q: 行锁和表锁在性能上差多少?

行锁的维护成本比表锁高。InnoDB 对每一行加锁都要在内存里维护锁结构,如果一次 UPDATE 涉及几万行,锁结构的开销就很可观了。表锁虽然并发差,但锁的维护成本极低——就一个标记。所以MySQL的默认存储引擎从MyISAM换成InnoDB后,并发高了,但单条SQL的锁开销也大了。

Q: GAP LOCK 在 RC 级别下存在吗?

不存在。RC级别下Gap Lock被禁用,只保留Record Lock。这也是RC并发更好的原因之一——锁的范围更小。代价就是RC下不能防幻读。

Q: 什么情况下 UPDATE 会锁住比预期更多的行?

两种情况最典型:一是 WHERE 条件没走索引,全表扫描锁住所有行(前面讲过了)。二是 WHERE 条件走了索引但索引选择性差(比如 status 字段只有两个值),InnoDB 可能会锁住大量相邻的索引记录。这种情况可以用 EXPLAINrows 字段来预判。

注意这里有个隔离级别的差异:RR 下索引选择性差会导致 Next-Key Lock 锁住大量间隙,因为 RR 会锁扫描到的索引范围。而 RC 下 Gap Lock 是关闭的,即使索引选择性差也只锁命中的行,不会额外锁间隙。所以从锁范围来看,RC 在这个场景下更友好。

Q: 怎么看当前谁在等锁、谁在持有锁?

MySQL 5.7 用 SELECT * FROM information_schema.INNODB_LOCK_WAITS,MySQL 8.0 用 SELECT * FROM performance_schema.data_lock_waits。8.0 的视图信息更全,能看到每个锁对应的线程ID和SQL。


总结

🎯 面试考点

  • 行锁 vs 全表锁定:行锁挂在索引上,没索引→全表扫描→锁住所有行,效果等同于表锁——这是线上最常见的锁故障
  • InnoDB 三种行锁:Record Lock(锁行)、Gap Lock(锁间隙)、Next-Key Lock(两者组合)
  • RR 下用 Next-Key Lock,RC 下只用 Record Lock —— Gap Lock 只在 RR 及以上级别生效
  • 意向锁:表级别的标记,告诉别人"有人在锁行",不是你手动加的
  • 锁诊断:PS 开启时用 data_lock_waits(精准);PS 关闭时用 SHOW ENGINE INNODB STATUS(通用)

今天就试一下:登录你的数据库跑一遍这三条SQL,看看有没有锁等待:

-- MySQL 8.0:performance_schema 视图(需 PS 开启)
SELECT * FROM performance_schema.data_lock_waits;
SELECT * FROM performance_schema.data_locks;

-- MySQL 5.7:information_schema 旧视图
-- SELECT * FROM information_schema.INNODB_LOCK_WAITS;
-- SELECT * FROM information_schema.INNODB_LOCKS;

-- 通用:InnoDB 引擎状态
SHOW ENGINE INNODB STATUS;

不需要等到出问题再看——现在看一眼"干净"的状态,等出问题的时候你才知道什么是"异常"。

下期预告:InnoDB MVCC 机制详解 —— 快照读、当前读、Undo 链!

全本合集《每天一个MySQL知识点,百日打怪升级》


有问题欢迎评论区交流,明天见!

相关文章
|
2月前
|
人工智能 自然语言处理 安全
Open Claw 2.6.4 Windows 一键部署完整教程(技术分享)
OpenClaw(昵称“小龙虾”)是2026年热门开源AI智能体,GitHub星标超28万。支持本地运行、零代码操作、跨平台部署,可理解自然语言指令,自动完成文件管理、数据处理、浏览器自动化等任务,一键安装,隐私安全。
|
20天前
|
人工智能 弹性计算 API
阿里云轻量应用服务器低成本部署OpenClaw方案:2核2G38元,2核4G199元,全球多地域可选
2026年阿里云轻量应用服务器低成本部署OpenClaw AI助理的方案:用户可通过每天10:00和15:00的限量抢购活动,以38元/年(2核2G/40G云盘)或9.9元/月、199元/年(2核4G/50G云盘)的价格入手服务器,预装OpenClaw镜像实现分钟级一键部署,免代码上手。部署后可通过Web UI或飞书、钉钉、QQ、企业微信等IM工具与AI智能体交互,并支持扩展Skill和自定义RPA流程。方案覆盖个人博客、AI应用开发等场景,大幅降低了AI Agent的技术与资金门槛,是低成本拥抱AI智能体的实用路径。
|
20天前
|
人工智能 安全 测试技术
阿里云JVS Claw是什么?JVS Claw能力、部署方式与部署流程参考
阿里云推出的AI智能体平台JVS Claw,基于OpenClaw框架打造,定位为"执行型智能体",解决了传统AI助手"只会说不会做"的痛点。JVS Claw支持云端与本地双部署模式,云端环境安全隔离、7×24小时在线,本地模式数据可控。产品提供体验版(7天免费)、匠心版(39元/月起)、大师版三档方案,适用于办公提效、代码开发、团队协作等场景。用户可通过自然语言指令驱动Clawbot执行邮件处理、代码生成、自动化测试等复杂任务,并支持自定义Skill扩展能力。39元/月起的定价大幅降低了AI Agent的使用门槛。
|
26天前
|
机器学习/深度学习 IDE 数据可视化
【2026最新】Spyder安装和使用保姆级教程(附安装包+图文步骤)
Spyder(Scientific Python Development Environment)是一款免费开源的Python IDE,专为数据科学、科学计算与机器学习设计。它融合代码编辑、调试、变量浏览与IPython交互式控制台、数据可视化等功能,界面类MATLAB,开箱即用NumPy、Pandas、Matplotlib等库,Anaconda用户可一键启用。(239字)
|
26天前
|
存储 缓存 安全
大模型应用:大模型响应缓存技术完全指南:TTL 缓存装饰器的设计与落地.112
本文详解大模型应用中缓存装饰器的实战实现,直击响应慢、成本高两大痛点。从基础缓存出发,逐步升级为支持TTL过期、线程安全、LRU淘汰、异常防护及哈希键优化的生产级方案,显著提升响应速度、降低Token消耗、增强系统稳定性。
202 7
|
1月前
|
机器学习/深度学习 存储 数据采集
大模型应用:慢病智能筛查与风险预警:XGBoost+规则引擎+大模型全解析.106
本文介绍“慢病智能筛查与风险预警”系统,融合XGBoost(精准打分)、规则引擎(合规校验)和大模型(自然语言解读),实现高效、准确、可解释的高血压等慢病风险分级,提升基层诊疗效率与规范性。
168 9
大模型应用:慢病智能筛查与风险预警:XGBoost+规则引擎+大模型全解析.106
|
26天前
|
SQL 关系型数据库 MySQL
【MySQL百日打怪升级第14天】 LIMIT 分页的性能优化:深分页到底慢在哪?
本文深入剖析MySQL深分页(如`LIMIT 100000,20`)性能瓶颈:本质是OFFSET导致全量扫描与丢弃,页码越深,扫描行数线性增长。详解三种实战优化方案——游标分页(高效稳定,需有序唯一字段)、延迟关联(兼容OFFSET,索引覆盖减回表)、范围分页(极简但场景受限),并附EXPLAIN对比与避坑指南。(239字)
167 6
|
12天前
|
人工智能 缓存 自然语言处理
千问云智能体Agent模型:Qwen3.7-Max列国产模型第一,在编程、推理能力提升,费用限制5折中
Qwen3.7-Max是阿里云2026年发布的旗舰智能体大模型,专注长周期自主执行,在编程(SWE-bench Pro 60.6分)、推理、办公自动化等能力上行业领先。国产模型全球盲测第一,支持MCP集成与Vibe Coding。现限时5折,输入/输出均降50%,并赠100万Tokens免费额度。快速体验:https://t.aliyun.com/U/fPVHqY
423 4
|
25天前
|
消息中间件 数据可视化 API
阿里云短信服务怎么接入?从签名、模板、API 到发送回执,一文讲清楚
本片文章将围绕阿里云短信服务的完整接入链路,拆解从资质申请、签名审核、模板配置、运营商报备,到 API 发送和状态回执的关键步骤,帮助产品经理、运营人员、技术负责人和开发者快速理解短信服务接入流程,提前做好上线准备。
312 5
|
20天前
|
机器学习/深度学习 数据采集 算法
PCB电路板缺陷检测数据集分享(适用于YOLO系列深度学习检测任务)
本数据集专为PCB缺陷检测设计,含1500张1024×1024图像(训练集1000张、验证集500张),标注6类常见缺陷(缺失孔、鼠咬痕、开路等),采用YOLO格式,开箱即用,适配YOLOv5/v8等主流模型,助力工业质检与AI研发。(239字)
211 6