每日一Go·MySQL-5、锁机制全解析

锁机制全解析:表锁 / 行锁 / 间隙锁 / Next-Key Lock 一次讲透.

线上 80% 的数据库性能问题 ,根本原因都不是 SQL,而是:锁冲突 & 锁等待 & 死锁。

真正的 MySQL 高手,拼的不是 SQL 技巧,而是:对锁机制的理解深度。

一、真实业务场景引入

你是否遇到过:

  • 明明走索引,SQL 依然慢?

  • QPS 不高,数据库却经常卡死?

  • 事务频繁阻塞?

  • 线上偶发死锁?

最终排查发现: 锁冲突严重。

问题来了:

  • MySQL 锁了什么?

  • 锁住了哪几行?

  • 锁为什么这么大?

二、MySQL 锁机制整体架构

从层级看,MySQL 锁分为:

  • 表锁(Table Lock)

  • 行锁(Row Lock, InnoDB)

从语义看,锁分为:

  • 共享锁(S Lock)

  • 排他锁(X Lock)

从实现看,InnoDB 行锁细分为:

  • Record Lock(记录锁)

  • Gap Lock(间隙锁)

  • Next-Key Lock(记录锁 + 间隙锁)

三、表锁(Table Lock)

锁住整张表,粒度最大,并发性能最低。

常见实用方式:

cs 复制代码
lock tables user read;
lock tables user write;
  1. 读锁(Read Lock / S)
bash 复制代码
lock tables user read;

效果:

  • 自己可读

  • 别人可读

  • 别人不可写

  1. 写锁(Write Lock / X)
cs 复制代码
lock tables user write;

效果:

  • 自己可读可写

  • 别人不可读不可写

  1. 表锁的应用场景
  • MyISAM 存储引擎

  • 备份

  • 批量离线任务

InnoDB 生产环境: 极少使用表锁

四、行锁(Row Lock)

InnoDB 的核心竞争力:行级锁

特点:

  • 锁粒度小

  • 并发能力强

  1. 行锁本质

InnoDB 的行锁:是加在索引上的,不是直接锁数据行。

  1. 行锁触发条件
sql 复制代码
select * from user where id = 10 for update;

前提:必须命中索引, 否则: 行锁 → 退化为 表级锁扫描

五、共享锁(S Lock) vs 排他锁(X Lock)

|-------|---|---|
| 锁类型 | 读 | 写 |
| 共享锁 S | ✔ | ✘ |
| 排他锁 X | ✔ | ✔ |

  1. 共享锁(S Lock)
cs 复制代码
select * from user where id=1 lock in share mode;

特点:允许多个事务同时读取, 禁止写.

  1. 排他锁(X Lock)
sql 复制代码
select * from user where id=1 for update;

特点: 独占锁, 禁止其他事务读 & 写.

六、Record Lock(记录锁)

锁住单条索引记录

sql 复制代码
select * from user where id = 10 for update;

锁住:id = 10 这一行

七、Gap Lock(间隙锁)

锁住索引之间的"空隙",防止插入新记录

sql 复制代码
select * from user where id > 5 and id < 10 for update;

已有数据:6,8

锁区间:(5,6), (6,8), (8,10)

任何事务都不能插入 7、9

八、Next-Key Lock(终极核心)

Next-Key Lock = Record Lock + Gap Lock

这是 InnoDB 在 RR 隔离级别的默认锁策略

示例拆解:

索引值:1, 5, 10, 20

sql 复制代码
select * from t where id = 10 for update;

加锁区间:(5,10]

不仅锁住 `10`,还锁住 `(5,10)` 整个区间。

九、为什么要有间隙锁 & Next-Key Lock?

  1. 解决幻读
cs 复制代码
事务 A:
select * from t where id between 5 and 10;

事务 B:
insert into t values(7,...);

如果没有间隙锁:A 前后两次读取结果不同 → 幻读

  1. RR 解决方案:MVCC + Next-Key Lock
  • MVCC 解决历史可见性

  • Next-Key Lock 锁住插入区间

从读取 & 写入两个方向同时封死幻读

十、锁与索引的致命关系(生产必懂)

是否命中索引,决定你锁几行。

  1. 命中索引:行锁

  2. 未命中索引:全表扫描 + 行行加锁 → 近似表锁

  3. 线上事故案例

sql 复制代码
update user set status=1 where mobile='13800138000';

如果 `mobile` 没索引:全表扫描 + 每行加锁 → 线上雪崩

十一、当前读 vs 快照读

  1. 快照读(无锁)
sql 复制代码
select * from user where id=1;

走:MVCC

  1. 当前读(加锁)
sql 复制代码
select * from user where id=1 for update;
update user ...
delete user ...

走:锁机制

十二、锁等待 & 死锁简析

  1. 锁等待
  • 事务 A 占锁

  • 事务 B 等锁 → 进入等待队列

可通过下面的语句查看:

css 复制代码
show engine innodb status\G
  1. 死锁经典案例

事务 A:

bash 复制代码
update t set v=1 where id=1;
update t set v=1 where id=2;

事务 B:

bash 复制代码
update t set v=1 where id=2;
update t set v=1 where id=1;

产生:循环等待 → 死锁

InnoDB 自动:回滚代价较小事务

十三、Go 工程实践规范(敲黑板)

  1. 事务中统一访问顺序

    所有业务统一按主键递增顺序访问,从根源杜绝死锁

  2. 事务一定要短

    事务时间 = 锁持有时间

  3. 所有 update / delete 必须走索引

工程规范:禁止非索引条件更新

十四、经典面试题

  1. MySQL 有哪些锁?

答:表锁、行锁、共享锁、排他锁、间隙锁、Next-Key Lock

  1. Next-Key Lock 是什么?

答:记录锁 + 间隙锁,用于解决幻读

  1. 为什么 update 没索引很危险?

答:全表扫描 + 行行加锁 → 并发雪崩

十五、今日总结

  1. 表锁 vs 行锁

  2. 共享锁 vs 排他锁

  3. 间隙锁 & Next-Key Lock

  4. 索引与锁的强耦合关系


源码地址*

1、https://pan.baidu.com/s/1B6pgLWfSgMngVeFfSTcPdg?pwd=jc1s


如果您喜欢这篇文章,请您(点赞、分享、亮爱心),万分感谢!

相关推荐
tachibana27 小时前
使用for range 的时候,它的地址会发生变化吗?
开发语言·后端·golang·go·语法
王中阳Go8 小时前
Go 后端转 AI:非科班怎么补?一张 3 列路线表(可保存)
开发语言·人工智能·后端·python·golang·大语言模型
阿里云云原生8 小时前
从页面渲染到智能体:ARMS Node.js 探针解锁 SSR + AI 全链路可观测
云原生
Nontee13 小时前
K3s vs K8s:差的不是功能,是交付方式
云原生·容器·kubernetes
做运维的阿瑞13 小时前
Docker常见故障全方位排查指南
运维·docker·云原生·容器
谢亮_vipxieliang13 小时前
云原生存储进阶:CSI 演进与 AI 工作负载的存储架构设计
人工智能·云原生
朱包林13 小时前
iTerm2终端配置指南
linux·javascript·python·云原生·容器·kubernetes·云计算
for_ever_love__14 小时前
LangChain Agentic RAG:让 Agent 自己决定要不要检索、检索几次
人工智能·golang·langchain·rag·langgraph·agentic·自适应检索
长安米粒贵15 小时前
K8s 探针配置错了,服务为什么会反复重启?聊聊 4 个常见误区
云原生