目录
[1.1 锁的定义与作用](#1.1 锁的定义与作用)
[1.2 MySQL 锁的粒度分类](#1.2 MySQL 锁的粒度分类)
[2.1 定义与核心作用](#2.1 定义与核心作用)
[2.2 语法与操作流程](#2.2 语法与操作流程)
[(2)数据备份(结合 mysqldump 工具)](#(2)数据备份(结合 mysqldump 工具))
[2.3 特点与局限性](#2.3 特点与局限性)
[优化方案(InnoDB 专属)](#优化方案(InnoDB 专属))
[3.1 表锁(Table Lock)](#3.1 表锁(Table Lock))
[3.1.1 语法](#3.1.1 语法)
[3.1.2 锁的兼容性规则](#3.1.2 锁的兼容性规则)
[3.1.3 测试案例演示](#3.1.3 测试案例演示)
[案例 1:读锁的兼容性](#案例 1:读锁的兼容性)
[案例 2:写锁的互斥性](#案例 2:写锁的互斥性)
[3.1.4 核心特点](#3.1.4 核心特点)
[3.2 元数据锁(MDL,Meta Data Lock)](#3.2 元数据锁(MDL,Meta Data Lock))
[3.2.1 MDL 的加锁规则](#3.2.1 MDL 的加锁规则)
[3.2.2 测试案例:MDL 锁的阻塞场景](#3.2.2 测试案例:MDL 锁的阻塞场景)
[3.2.3 查看 MDL 锁状态](#3.2.3 查看 MDL 锁状态)
[3.2.4 核心注意点](#3.2.4 核心注意点)
[3.3 意向锁(Intention Lock)](#3.3 意向锁(Intention Lock))
[3.3.1 核心问题:为什么需要意向锁?](#3.3.1 核心问题:为什么需要意向锁?)
[3.3.2 意向锁的分类与兼容性](#3.3.2 意向锁的分类与兼容性)
[3.3.3 查看意向锁状态](#3.3.3 查看意向锁状态)
[3.3.4 测试案例:意向锁与表锁的兼容性](#3.3.4 测试案例:意向锁与表锁的兼容性)
[4.1 行锁(Record Lock)](#4.1 行锁(Record Lock))
[4.1.1 行锁的分类与兼容性](#4.1.1 行锁的分类与兼容性)
[4.1.2 不同 SQL 对应的行锁类型](#4.1.2 不同 SQL 对应的行锁类型)
[4.1.3 测试案例演示](#4.1.3 测试案例演示)
[案例 1:共享锁的兼容性](#案例 1:共享锁的兼容性)
[案例 2:排他锁的互斥性](#案例 2:排他锁的互斥性)
[案例 3:无索引导致行锁升级为表锁](#案例 3:无索引导致行锁升级为表锁)
[4.2 间隙锁(Gap Lock)](#4.2 间隙锁(Gap Lock))
[4.2.1 核心作用](#4.2.1 核心作用)
[4.2.2 触发场景](#4.2.2 触发场景)
[4.2.3 测试案例:唯一索引等值查询(不存在的记录)](#4.2.3 测试案例:唯一索引等值查询(不存在的记录))
[4.3 临键锁(Next-Key Lock)](#4.3 临键锁(Next-Key Lock))
[4.3.1 临键锁的锁定规则](#4.3.1 临键锁的锁定规则)
[4.3.2 测试案例:唯一索引范围查询](#4.3.2 测试案例:唯一索引范围查询)
[4.3.3 核心注意点](#4.3.3 核心注意点)
[5.1 锁机制核心对比](#5.1 锁机制核心对比)
[5.2 实践优化建议](#5.2 实践优化建议)
前言
在数据库并发访问场景中,锁是保证数据一致性与完整性的核心机制。MySQL 作为主流关系型数据库,提供了多层次、多粒度的锁机制,适配不同业务场景的并发需求。本文基于 MySQL 锁的核心知识,从锁的分类、每种锁的原理、语法、使用场景、测试案例等维度,逐字逐句拆解文档细节,带你全面掌握 MySQL 锁机制的底层逻辑与实操技巧。
一、锁的核心概述
1.1 锁的定义与作用
锁是计算机协调多个进程 / 线程并发访问共享资源的机制。在 MySQL 中,数据作为核心共享资源,面临多用户同时读写的场景,锁的核心作用是:
- 保证并发操作的数据一致性(避免脏读、不可重复读、幻读);
- 协调冲突操作(如同时修改同一行数据),避免数据损坏;
- 平衡并发性能与数据安全性(不同锁粒度对应不同并发能力)。
1.2 MySQL 锁的粒度分类
MySQL 的锁按锁定粒度可分为三类,粒度从大到小依次为:
| 锁类型 | 锁定范围 | 并发度 | 冲突概率 | 适用场景 |
|---|---|---|---|---|
| 全局锁 | 整个数据库实例 | 最低 | 最高 | 全库逻辑备份 |
| 表级锁 | 整张表 | 中等 | 中等 | 读多写少、表结构变更场景 |
| 行级锁 | 单条记录 | 最高 | 最低 | 高并发读写、修改少量数据场景 |
这种分级设计的核心思想是:锁粒度越小,并发度越高,但锁的维护成本越高;锁粒度越大,并发度越低,但维护成本越低。
二、全局锁
2.1 定义与核心作用
全局锁是对整个 MySQL 实例 加锁的机制,加锁后实例进入只读状态:
- 允许执行 DQL(查询)操作;
- 阻塞所有 DML(增删改)、DDL(表结构变更)及事务提交操作。
其典型使用场景是全库逻辑备份,目的是获取数据库的一致性视图,避免备份过程中数据被修改导致备份数据不一致(如备份库存表时,业务同时扣减库存,导致备份的库存数据与订单数据不匹配)。
2.2 语法与操作流程
(1)加全局锁
flush tables with read lock; -- 加全局锁,实例进入只读状态
(2)数据备份(结合 mysqldump 工具)
mysqldump -uroot -p1234 itcast > itcast_backup.sql -- 备份全库数据
(3)释放全局锁
unlock tables; -- 手动释放锁
# 或关闭客户端连接(自动释放)
2.3 特点与局限性
全局锁是一种 "重量级" 锁,存在明显局限性:
- 主库备份影响业务:备份期间主库无法执行更新操作,业务基本停摆;
- 从库备份导致主从延迟:备份期间从库无法同步主库的 binlog 日志,导致主从数据不一致。
优化方案(InnoDB 专属)
InnoDB 引擎支持不加锁的一致性备份 ,通过--single-transaction参数实现,利用事务的 MVCC 机制获取一致性视图,无需锁全库:
mysqldump --single-transaction -uroot -p1234 itcast > itcast_backup.sql
三、表级锁
表级锁是 MySQL 中最常用的锁类型之一,锁定粒度为整张表,支持 MyISAM、InnoDB、BDB 等多种存储引擎。按功能可分为表锁、元数据锁(MDL)、意向锁三类,每类锁的作用与使用场景截然不同。
3.1 表锁
表锁是最基础的表级锁,分为表共享读锁(Read Lock) 和表独占写锁(Write Lock),语法简单,锁机制直观。
3.1.1 语法
- 加锁:
lock tables 表名 [,表名...] read/write; - 释放锁:
unlock tables;(或关闭客户端连接)
3.1.2 锁的兼容性规则
表锁的核心特点是 "读锁兼容、写锁互斥",具体兼容性如下:
表格
| 当前锁类型 | 其他事务请求读锁 | 其他事务请求写锁 |
|---|---|---|
| 读锁(Read) | 兼容(可共存) | 冲突(阻塞) |
| 写锁(Write) | 冲突(阻塞) | 冲突(阻塞) |
3.1.3 测试案例演示
案例 1:读锁的兼容性
-
客户端 1(加读锁):
lock tables tb_user read; -- 对tb_user加读锁 select * from tb_user; -- 允许执行查询 update tb_user set age=25 where id=1; -- 禁止执行更新,报错 -
客户端 2(请求读锁):
select * from tb_user; -- 允许执行(读锁兼容) update tb_user set age=25 where id=1; -- 阻塞(读锁与写锁冲突)
案例 2:写锁的互斥性
-
客户端 1(加写锁):
lock tables tb_user write; -- 对tb_user加写锁 update tb_user set age=25 where id=1; -- 允许执行更新 select * from tb_user; -- 允许执行查询 -
客户端 2(请求读 / 写锁):
select * from tb_user; -- 阻塞(写锁与读锁冲突) update tb_user set age=25 where id=1; -- 阻塞(写锁与写锁冲突)
3.1.4 核心特点
- 读锁:共享锁,多个事务可同时加读锁,仅允许查询,禁止修改;
- 写锁:排他锁,仅一个事务可加写锁,允许查询和修改,阻塞所有其他锁请求;
- 适用场景:MyISAM 引擎(默认表锁)、读多写少的业务(如静态数据查询)。
3.2 元数据锁(MDL,Meta Data Lock)
MDL 是 MySQL5.5 引入的表级锁,自动加锁、无需手动操作,核心作用是维护表元数据(表结构)的一致性,避免 DML 与 DDL 冲突(如一个事务正在更新数据,另一个事务修改表结构)。
3.2.1 MDL 的加锁规则
MySQL 会根据 SQL 操作类型自动添加 MDL 锁,具体规则如下:
| SQL 操作类型 | MDL 锁类型 | 说明 |
|---|---|---|
| select、select...lock in share mode | 共享锁(SHARED_READ) | 与其他共享锁兼容,与排他锁互斥 |
| insert、update、delete、select...for update | 共享锁(SHARED_WRITE) | 与其他共享锁兼容,与排他锁互斥 |
| alter table、drop table 等 DDL 操作 | 排他锁(EXCLUSIVE) | 与所有 MDL 锁互斥 |
3.2.2 测试案例:MDL 锁的阻塞场景
-
客户端 1(执行 DML,加 SHARED_WRITE 锁):
begin; update tb_user set age=25 where id=1; -- 加SHARED_WRITE锁 -- 不提交事务 -
客户端 2(执行 DDL,请求 EXCLUSIVE 锁):
alter table tb_user add column addr varchar(50); -- 阻塞,直到客户端1事务提交
3.2.3 查看 MDL 锁状态
通过performance_schema库查询当前 MDL 锁持有情况:
select object_type, object_schema, object_name, lock_type, lock_duration
from performance_schema.metadata_locks;
3.2.4 核心注意点
- MDL 锁默认在事务结束后释放(而非 SQL 执行完毕);
- 长事务会导致 MDL 锁长期持有,阻塞 DDL 操作,需避免长时间未提交的事务。
3.3 意向锁
意向锁是 InnoDB 引擎专属的表级锁,自动添加、无需手动操作,核心作用是 "减少表锁与行锁的冲突检查成本",避免表锁检查时逐行判断行锁状态。
3.3.1 核心问题:为什么需要意向锁?
假设没有意向锁,客户端 1 对表中某行加行锁后,客户端 2 请求表锁时,需要逐行检查表中所有记录是否加行锁,效率极低(尤其大数据量表)。意向锁的引入,让表锁检查只需判断 "表是否有意向锁",无需逐行扫描。
3.3.2 意向锁的分类与兼容性
意向锁分为两类,兼容性规则如下:
| 意向锁类型 | 作用 | 与表读锁(Read) | 与表写锁(Write) |
|---|---|---|---|
| 意向共享锁(IS) | 事务准备对表中某行加共享锁(S 锁) | 兼容 | 冲突 |
| 意向排他锁(IX) | 事务准备对表中某行加排他锁(X 锁) | 冲突 | 冲突 |
- 意向锁的添加时机:事务执行 DML 操作(insert/update/delete/select...for update 等)时,InnoDB 自动为表加对应意向锁;
- 意向锁的释放时机:事务提交或回滚后自动释放。
3.3.3 查看意向锁状态
通过performance_schema库查询意向锁与行锁的持有情况:
select object_schema, object_name, index_name, lock_type, lock_mode, lock_data
from performance_schema.data_locks;
3.3.4 测试案例:意向锁与表锁的兼容性
-
客户端 1(执行 DML,加 IX 锁 + 行锁):
begin; update tb_user set age=25 where id=1; -- 自动加IX锁(表级)+ 行锁(id=1) -
客户端 2(请求表读锁):
lock tables tb_user read; -- 阻塞(IX锁与表读锁冲突)
四、行级锁
行级锁是 InnoDB 引擎的核心锁机制,锁定粒度为单条记录,并发度最高,是高并发业务的首选。InnoDB 的行锁基于索引实现,而非直接锁定记录,核心分为行锁(Record Lock)、间隙锁(Gap Lock)、临键锁(Next-Key Lock) 三类。
4.1 行锁(Record Lock)
行锁是最基础的行级锁,锁定单个行记录,防止其他事务对该记录执行 update/delete 操作,支持 RC(读已提交)、RR(可重复读)隔离级别。

4.1.1 行锁的分类与兼容性
InnoDB 实现了两种行锁,兼容性规则如下:
| 当前锁类型 | 其他事务请求共享锁(S) | 其他事务请求排他锁(X) |
|---|---|---|
| 共享锁(S) | 兼容(可共存) | 冲突(阻塞) |
| 排他锁(X) | 冲突(阻塞) | 冲突(阻塞) |
4.1.2 不同 SQL 对应的行锁类型
| SQL 操作 | 行锁类型 | 说明 |
|---|---|---|
| select(普通查询,不加锁) | 无 | 快照读(MVCC),非锁定读 |
| select...lock in share mode | 共享锁(S) | 手动加共享锁,允许其他事务读,禁止写 |
| select...for update | 排他锁(X) | 手动加排他锁,禁止其他事务读 / 写 |
| insert、update、delete | 排他锁(X) | 自动加排他锁 |
4.1.3 测试案例演示
案例 1:共享锁的兼容性
-
客户端 1(加共享锁):
begin; select * from stu where id=1 lock in share mode; -- 对id=1加S锁 -
客户端 2(请求共享锁):
begin; select * from stu where id=1 lock in share mode; -- 兼容,正常执行 select * from stu where id=3 lock in share mode; -- 对其他行加锁,正常执行 update stu set age=20 where id=1; -- 冲突,阻塞(S锁与X锁冲突)
案例 2:排他锁的互斥性
-
客户端 1(加排他锁):
begin; update stu set age=20 where id=1; -- 对id=1加X锁 -
客户端 2(请求排他锁):
begin; update stu set age=20 where id=1; -- 冲突,阻塞 select * from stu where id=1 for update; -- 冲突,阻塞
案例 3:无索引导致行锁升级为表锁
InnoDB 的行锁基于索引实现,若查询条件无索引,行锁会升级为表锁,并发性能骤降:
-
表结构(name 字段无索引):
CREATE TABLE `stu` ( `id` int NOT NULL PRIMARY KEY AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `age` int NOT NULL ) ENGINE=InnoDB; -
客户端 1(无索引查询,加表锁):
begin; update stu set age=20 where name='lily'; -- name无索引,行锁升级为表锁 -
客户端 2(修改其他行,阻塞):
update stu set age=20 where id=3; -- 阻塞(表锁导致) -
优化方案:为 name 字段建立索引,行锁正常生效,不升级为表锁。
4.2 间隙锁
间隙锁是 InnoDB 在 RR 隔离级别下为解决 "幻读" 引入的锁机制,锁定索引记录之间的间隙(不含记录本身),防止其他事务在间隙中插入数据。

4.2.1 核心作用
幻读是指同一事务中,多次执行相同范围查询,返回的记录数不一致(其他事务插入了符合条件的记录)。间隙锁通过锁定间隙,禁止插入操作,从而避免幻读。
4.2.2 触发场景
间隙锁仅在RR 隔离级别下生效,触发条件为:
- 索引上的等值查询(唯一索引):对不存在的记录加锁时,优化为间隙锁;
- 索引上的等值查询(非唯一索引):向右遍历到不满足条件的记录时,间隙锁生效;
- 索引上的范围查询(唯一索引 / 非唯一索引):锁定范围及相邻间隙。
4.2.3 测试案例:唯一索引等值查询(不存在的记录)
-
表数据(id 为主键,唯一索引):
id: 1, 3, 8, 11, 19, 25 -
客户端 1(加间隙锁):
begin; select * from stu where id=5 for update; -- id=5不存在,锁定间隙(3,8) -
客户端 2(插入间隙数据,阻塞):
insert into stu (name, age) values ('jack', 25); -- id自动递增为6,落在(3,8)间隙,阻塞 insert into stu (name, age) values ('tom', 25); -- id自动递增为7,落在(3,8)间隙,阻塞 insert into stu (name, age) values ('amy', 25); -- id自动递增为26,落在(25,+∞)间隙,正常执行
4.3 临键锁
临键锁是行锁 + 间隙锁的组合,锁定 "索引记录 + 相邻间隙",是 InnoDB 在 RR 隔离级别下的默认锁机制(范围查询时触发),既能防止幻读,又能保证数据一致性。

4.3.1 临键锁的锁定规则
临键锁的锁定范围遵循 "左闭右开" 原则,例如索引记录为 1,3,8 时,临键锁的锁定范围为:(-∞,1]、(1,3]、(3,8]、(8,+∞]。
4.3.2 测试案例:唯一索引范围查询
-
客户端 1(加临键锁):
begin; select * from stu where id >=19 for update; -- 锁定范围(11,19]、(19,25]、(25,+∞] -
客户端 2(插入 / 修改锁定范围数据,阻塞):
insert into stu (name, age) values ('lucy', 30); -- id=26,落在(25,+∞],阻塞 update stu set age=30 where id=25; -- 落在(19,25],阻塞 update stu set age=30 where id=11; -- 落在(8,11],正常执行
4.3.3 核心注意点
- 临键锁仅在 RR 隔离级别下生效,RC 隔离级别下会退化为主键 / 唯一索引的行锁;
- 间隙锁和临键锁的唯一目的是防止幻读,它们之间可以共存(多个事务可对同一间隙加锁)。
五、锁机制的核心总结与实践建议
5.1 锁机制核心对比
表格
| 锁类型 | 锁定粒度 | 并发度 | 适用引擎 | 核心场景 | 注意事项 |
|---|---|---|---|---|---|
| 全局锁 | 全库 | 最低 | 所有引擎 | 全库逻辑备份 | 避免在主库长期持有 |
| 表锁 | 整张表 | 中等 | MyISAM、InnoDB | 读多写少、静态数据 | 写锁阻塞所有操作 |
| MDL 锁 | 整张表(元数据) | 中等 | 所有引擎 | 表结构保护 | 避免长事务持有 MDL 锁 |
| 意向锁 | 整张表(标记) | 中等 | InnoDB | 行锁与表锁冲突优化 | 自动添加,无需手动操作 |
| 行锁 | 单条记录 | 最高 | InnoDB | 高并发读写、修改少量数据 | 基于索引,无索引升级为表锁 |
| 间隙锁 / 临键锁 | 索引间隙 + 记录 | 最高 | InnoDB | RR 隔离级别下防幻读 | 仅 RR 隔离级别生效 |
5.2 实践优化建议
-
选择合适的锁粒度:
- 读多写少、表结构稳定:使用表锁(MyISAM)或 InnoDB 的表锁;
- 高并发读写、修改少量数据:使用 InnoDB 行锁(确保查询条件有索引)。
-
避免锁冲突与升级:
- 为 DML 操作的查询条件字段建立索引,防止行锁升级为表锁;
- 避免长事务,减少锁持有时间,降低阻塞概率;
- 读操作优先使用普通 select(快照读,不加锁),避免手动加共享锁。
-
隔离级别与锁的配合:
- 需防幻读:使用 RR 隔离级别(依赖间隙锁 / 临键锁);
- 并发优先,可接受幻读:使用 RC 隔离级别(行锁不升级,并发更高)。
-
监控锁状态:
- 定期查询 MDL 锁、行锁持有情况,排查长期阻塞的事务;
- 利用
show engine innodb status查看 InnoDB 锁等待信息。