1. 从内存锁到数据库锁:为什么我们需要重新审视锁
先聊一个近期让我印象很深的问题背景。某个周五晚上,业务群里突然有人说“订单列表打不开了”,紧接着就是“后台保存也报错了”。我登陆数据库一看,SHOW PROCESSLIST 里挂了一排 Waiting for table metadata lock。当时的第一反应不是去看慢查询,而是去查 information_schema.innodb_trx,果然发现一条几乎跑了半小时的事务还死死攥着某张表的元数据锁。那一刻我意识到,很多人(包括我)在应用层写惯了 synchronized 和 ReentrantLock,一进入 MySQL 的世界,反而对锁的玩法和坑特别陌生。
这也正好是我这篇“锁机制(下)”想写透的事。上一篇我们聊了并发编程中的锁:偏向锁、轻量级锁、自旋锁、AQS,还有分布式锁那套跨进程互斥的思路。但那次聊完以后,我收到不少反馈,说真正让他们头疼的其实是数据库层面的锁,尤其是 MySQL 的锁表机制。一条简单的 UPDATE 没走索引,居然能把整张表冻住;一个长时间不提交的事务,居然能让所有写操作排队。这些现象背后的原理,和应用层的锁模型有联系,但并不同构。
先说一个我的核心结论:数据库锁不是“锁数据行”那么简单,它锁的是索引记录、区间、元数据甚至整张表,锁的粒度取决于存储引擎、隔离级别、索引条件和执行计划。而 MySQL 里最常见的故障形态就是“锁表”,表现为某个会话持有排他锁,后续所有对该表的写入都被堵住,连接数迅速累积,最后整个服务不可用。
很多开发者学习锁机制时,容易踩三个误区。
第一个误区是把“MySQL 锁”和“InnoDB 行锁”划等号。实际上 MyISAM 引擎只有表锁,InnoDB 才有真正的行锁;而且 InnoDB 的行锁必须建立在索引之上,如果查询无法使用索引,行锁就会退化成大范围的锁,看起来和表锁没什么区别。
第二个误区是认为“事务提交后锁一定会立刻释放”。这句话大方向没问题,但间隙锁和临键锁在 REPEATABLE READ 隔离级别下,可能在事务提交后才彻底释放。如果事务体很大,持锁时间就会被拉长,等待队列里的事务就会堆积。
第三个误区是认为死锁是小概率事件。在高并发下,只要多个事务对多张表或者多个索引的加锁顺序不一致,死锁几乎必然出现。问题不在于“会不会死锁”,而在于你有没有死亡检测机制,以及日志记录能不能支撑你快速定位。
所以,对锁机制的理解,我建议至少分成三层:进程内锁、数据库存储引擎锁、跨进程分布式协调锁。这篇聚焦中间这一层:以 InnoDB 为主线,把表锁、行锁、间隙锁、元数据锁,还有锁等待和死锁的排查方法,全部串起来讲一遍。
理解这些内容之后,你至少能拿到三样东西:第一,以后再遇到“数据库卡住”这类故障,知道该去哪个视图查锁;第二,能根据 EXPLAIN 的结果判断一条 SQL 是否会造成锁表;第三,在设计高并发事务时,清楚为什么要控制事务大小、为什么要保证索引有效、为什么加锁顺序最好一致。
这部分的重点不是背参数,而是建立一个判断框架:任何锁的引入,都是并发控制与性能之间的取舍。你看到锁等待,第一反应不该是“杀掉进程”,而是“为什么这个事务需要持有这么久的锁”。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. MySQL 锁表机制:一条 UPDATE 让全库卡顿的真相
我们经常在故障复盘时听到“锁表”这个词,但“锁表”在 MySQL 里其实有好几种完全不同的实现源头。我把它们拆开讲,不然排查时会走很多弯路。
2.1 Server 层表锁与存储引擎层锁是两套体系
MySQL 的架构是分层的:Server 层负责连接管理、SQL 解析、优化和执行;存储引擎层负责数据读写和事务控制。表锁这个操作,在 Server 层和 InnoDB 层都存在,但它们不是同一个东西。
Server 层的表锁,最常见的是手动执行 LOCK TABLES table_name WRITE 这种命令。一旦执行,其他会话对该表的读写都会被阻塞,直到执行 UNLOCK TABLES 或会话断开。这种锁在日常业务代码里一般不会主动使用,但在一些维护脚本中偶尔能看到。如果你在 PROCESSLIST 里看到 Waiting for table level lock,多半就和这类锁相关。
InnoDB 引擎层的真正语义是“索引锁”和“范围锁”,它理论上不排斥并发读写。但在实际执行中,如果一条 SQL 无法走索引,InnoDB 会在扫描每一行时都加上锁,最终锁定的记录覆盖了大量数据页,行为上等同于锁表。更麻烦的是,这种“隐式锁表”不像显式 LOCK TABLES 那么直观,从 SHOW PROCESSLIST 里看到的只是普通的 Updating 或 Locked,如果不结合 EXPLAIN 和锁等待视图,很容易误判为慢 SQL。
2.2 锁模式:读写锁的基本盘
MySQL 里的锁模式虽然数量不少,但核心规律可以简化成一句话:读读共享、读写互斥、写写互斥。InnoDB 层面的锁类型包括:共享锁(S)、排他锁(X)、意向共享锁(IS)、意向排他锁(IX)。其中意向锁是表级锁,用来快速判断表中是否已有行级锁,避免逐行检查。
| 锁模式 | 简称 | 兼容性 | 典型场景 |
|---|---|---|---|
| 共享锁 | S | 与S兼容,与X互斥 | SELECT ... LOCK IN SHARE MODE |
| 排他锁 | X | 与S/X都互斥 |
