Mysql:有哪些锁?

MySQL 中的锁不是一个平铺的分类,可以从三个维度理解:

复制代码
锁的范围:全局锁、表锁、行锁
锁的模式:共享锁、排他锁、意向锁
锁住的对象:记录、间隙、元数据、自增计数器等

下面主要以最常用的 InnoDB 为例。

一、共享锁和排他锁

1. 共享锁 S Lock

也叫读锁。事务获得共享锁后可以读取数据:

复制代码
SELECT *
FROM users
WHERE id = 1
FOR SHARE;

特点:

复制代码
S 锁和 S 锁兼容
S 锁和 X 锁冲突

多个事务可以同时获得同一记录的共享锁,但不能同时修改它。

2. 排他锁 X Lock

也叫写锁。用于修改或删除数据:

复制代码
SELECT *
FROM users
WHERE id = 1
FOR UPDATE;

UPDATE users SET age = 20 WHERE id = 1;

DELETE FROM users WHERE id = 1;

特点:

复制代码
X 锁与 S 锁冲突
X 锁与 X 锁冲突

一个事务持有排他锁时,其他事务不能再获得冲突锁。InnoDB 的基本行锁模式就是 S 锁和 X 锁。

二、意向锁

意向锁是表级别的标记,表示事务准备在表中的某些记录上加什么锁。

意向锁 含义
IS 准备在某些记录上加共享锁
IX 准备在某些记录上加排他锁

例如:

复制代码
UPDATE users SET age = 20 WHERE id = 1;

大致会产生:

复制代码
users 表上加 IX 锁
id=1 对应索引记录上加 X 锁

意向锁不是为了锁住整张表的数据,而是让 MySQL 快速判断表级锁是否与已有行锁冲突。

兼容关系可以简化为:

IS IX S X
IS 兼容 兼容 兼容 冲突
IX 兼容 兼容 冲突 冲突
S 兼容 冲突 兼容 冲突
X 冲突 冲突 冲突 冲突

三、InnoDB 的记录锁

InnoDB 所谓的"行锁",本质上通常是索引记录锁

复制代码
UPDATE users
SET name = 'Tom'
WHERE id = 10;

如果 id 是主键,就会锁住主键索引中 id=10 的记录。

复制代码
主键索引:1  5  [10]  20
                 ↑
               被锁定

因此索引非常重要。如果没有合适索引,MySQL可能需要扫描并锁住大量索引记录,实际效果可能接近锁住整张表。

四、间隙锁 Gap Lock

间隙锁锁住的是两个索引值之间的空隙,而不是现有记录。

假设索引中有:

复制代码
10、20、30

间隙包括:

复制代码
(-∞,10)
(10,20)
(20,30)
(30,+∞)

如果锁住 (10,20),其他事务不能在这个范围插入 11~19,但通常不影响修改已经存在的 1020

间隙锁主要用于防止范围当前读出现幻行。

五、临键锁 Next-Key Lock

临键锁是:

复制代码
Next-Key Lock = 记录锁 + 记录前面的间隙锁

例如锁住:

复制代码
(10,20]

意味着同时锁住:

复制代码
间隙:(10,20)
记录:20

在 RR 隔离级别下执行范围当前读:

复制代码
SELECT *
FROM users
WHERE age BETWEEN 20 AND 30
FOR UPDATE;

InnoDB 可能使用 Next-Key Lock 锁住相关索引记录及间隙,阻止其他事务修改已有记录或向查询范围插入新记录。

六、插入意向锁

插入意向锁是一种特殊的间隙锁,表示事务准备向某个间隙插入记录。

索引中存在:

复制代码
10、20

两个事务分别执行:

复制代码
INSERT INTO users(id) VALUES (12);
INSERT INTO users(id) VALUES (18);

虽然都向 (10,20) 插入,但插入位置不同,通常可以并发。

如果该间隙已经被其他事务的间隙锁或 Next-Key Lock 锁住,插入意向锁就需要等待。

七、表锁

可以显式锁住整张表:

复制代码
LOCK TABLES users READ;
LOCK TABLES users WRITE;

UNLOCK TABLES;
  • READ:当前会话可以读,不能写,其他会话也可以读。
  • WRITE:当前会话可以读写,其他会话通常不能访问。

InnoDB 主要使用行级锁,一般不建议通过 LOCK TABLES 手动锁住整张 InnoDB 表,因为会明显降低并发能力。

八、元数据锁 MDL

MDL 的全称是 Metadata Lock,用来保护表结构等数据库对象。

事务执行:

复制代码
START TRANSACTION;
SELECT * FROM users;

会获得 users 的元数据锁。如果事务一直没有结束,另一个会话执行:

复制代码
ALTER TABLE users ADD COLUMN phone VARCHAR(20);

可能一直等待,并显示:

复制代码
Waiting for table metadata lock

MDL 防止一个事务使用表时,另一个会话突然删除或改变表结构。事务中的 MDL 通常要等到 COMMITROLLBACK 才释放,因此未提交的长事务经常造成 DDL 阻塞。

九、AUTO-INC 锁

这是 InnoDB 为 AUTO_INCREMENT 分配自增值使用的特殊机制:

复制代码
CREATE TABLE orders (
    id BIGINT PRIMARY KEY AUTO_INCREMENT
);

多个事务同时插入时,InnoDB 需要协调自增值的分配。是否使用表级 AUTO-INC 锁、持有多久,取决于插入类型和 innodb_autoinc_lock_mode。当前模式支持传统、连续和交错三种分配策略。

十、全局锁和备份锁

全局读锁可以通过以下命令获得:

复制代码
FLUSH TABLES WITH READ LOCK;

它会对整个 MySQL 实例加全局读锁,通常允许查询但阻止数据修改,常用于某些备份场景:

复制代码
UNLOCK TABLES;

此外,LOCK INSTANCE FOR BACKUP 可以获得实例级备份锁,允许普通 DML 继续执行,但阻止可能破坏备份一致性的部分操作。

相关推荐
李可以量化1 小时前
Tornado 从了解到精通(一)下:异步与非阻塞 IO 核心原理
java·数据库·tornado
hey_sml1 小时前
MySQL常用操作速记:从字符集到游标全解析
java·开发语言·mysql
云技纵横1 小时前
线上接口突然超时,怎么判断卡在 Nginx、线程池、连接池还是 SQL?
后端·sql·mysql
VALENIAN瓦伦尼安教学设备2 小时前
ASHOOTER激光对中仪如何通过颜色确定调整是否合适
数据库·嵌入式硬件·算法
Wang's Blog2 小时前
AI Agent白手起家58: 项目可观测性——用 LangSmith 实现全链路追踪
数据库·人工智能
SL_staff2 小时前
JVS数字底座实践:如何复用企业文档能力快速构建知识类应用
java·数据库·程序员
技术长镜头3 小时前
别再死记 Record、Gap、Next-Key:沿一条 SQL 看懂 InnoDB 锁
后端·mysql
qq_185198693 小时前
SpingBoot3入门学习-Web开发-三
java·数据库·学习
农村小镇哥3 小时前
C#读取CSV文件的方法
服务器·数据库·c#