文章目录
数据库的锁介绍
按粒度分类:
- 表锁
表锁是数据库中最基础的一种锁,作用范围是整张表。当事务对一张表加上表锁后,其他事务对该表的访问(读或写)将受到限制,直到锁释放。innodb和myisam数据库引擎都支持表锁。 - 行锁
行锁是指数据库在执行写操作或带锁读操作时,只锁定被操作的行,不会锁住整张表,是一种细粒度锁。innodb支持行锁 - 页锁
页锁(Page Lock) 是一种中等粒度的锁机制,它介于 行锁 和 表锁 之间,锁定的是数据页(即一组行记录的集合)。每页通常包含多行数据(如一个数据块 4KB 或 8KB)。页锁并不锁单独的行或整张表,而是锁整个数据页。
按读写分类:
- 共享锁:允许多个事务同时读取,但不能修改
- 排他锁:只能有一个事务拥有,同时禁止其他读写
InnoDB 特有的锁:
- 行锁(Record Lock) 锁定某一行记录(SELECT … FOR UPDATE)。
- 间隙锁(Gap Lock) 锁住两个记录之间的“空隙”,防止幻读。
- 临键锁(Next-Key Lock) 行锁 + 间隙锁,用于锁定当前行和前一个间隙(防止幻读)。
- 插入意向锁(Insert Intention Lock) 插入操作时加的表级锁,配合间隙锁使用,防止插入冲突。
- 意向锁(IS、IX) 表示事务打算加某种行锁(S或X),用于提高锁检测性能。
锁的行为场景
- MVCC 读共享:
SELECT(普通查询)不具有锁,可在读已提交和可重复读的隔离级别下产生(MVCC 快照读) 快照读,不加锁,提高并发性能。 - LOCK IN SHARD MODE
SELECT … LOCK IN SHARE MODE 加锁 行级共享锁(S 锁) + 意向共享锁(IS) 允许其他事务读,不允许写 - FOR UPDATE
SELECT … FOR UPDATE 加锁 行级排他锁(X 锁) + 意向排他锁(IX) 当前读,禁止其他事务读写该行。
4.排他锁
UPDATE / DELETE 加锁 行级排他锁(X 锁) + 间隙锁(如有范围) 当前读 + 锁住目标数据及可能范围。
INSERT 加锁 插入意图锁 + 间隙锁 防止多个事务同时插入到相同位置。
REPLACE 加锁 删除旧记录(X 锁)+ 插入新记录(意图锁) 是 INSERT + DELETE 的复合操作。
INSERT IGNORE 加锁 有主键/唯一约束 → 先加 S 锁查重 不存在则插入,存在则忽略 - 表锁
ALTER / DROP / TRUNCATE 加锁,表级元数据锁(MDL 写锁) 阻塞其他所有读写。
CREATE INDEX / ANALYZE TABLE 加锁 表级元数据锁(MDL 写锁) DDL 操作期间,阻塞所有 DML 查询。
事务隔离级别影响加锁行为
- READ UNCOMMITTED 读未提交,所有查询都不加锁,能看到其他事务尚未提交的修改(脏读),不会触发行锁、间隙锁、Next-Key Lock。
- READ COMMITTED 读已提交,当前读(SELECT FOR UPDATE / UPDATE / DELETE) 加 行锁(Record Lock),不加间隙锁 → 幻读可能发生,快照读(普通 SELECT)每次都读取最新版本。
- 当前读(SELECT FOR UPDATE / UPDATE / DELETE) 加 行锁(Record Lock),不加间隙锁 → 幻读可能发生,快照读(普通 SELECT)每次都读取最新版本。
- SERIALIZABLE(串行化),所有读取都加共享锁(S 锁),强制串行执行,所有事务都像排队一样执行,严格防止幻读,但并发性差。
案例
行锁案例
--创建表
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(50)
) ENGINE=InnoDB;
BEGIN;
SELECT * FROM users WHERE id = 1 FOR UPDATE;
-- 加上了 id=1 的行锁(排他锁)
BEGIN;
UPDATE users SET name = 'Tom' WHERE id = 1;
-- 阻塞:必须等待 A 提交
间隙锁 gap Lock
-- 如果数据为 [10, 20, 30]
SELECT * FROM users WHERE id BETWEEN 10 AND 20 FOR UPDATE;
--间隙锁的范围 gap(10, 20)
--行锁 row(20)
主键、唯一索引、普通索引对行锁的影响
- 使用主键、唯一索引精确查找 精确行锁。
- 范围查找(BETWEEN, LIKE, >) 行锁 + 间隙锁。
- 无索引(全表扫描),表锁。
- 普通索引或非唯一索引,行锁或多个行锁。
事务的隔离级别对锁的影响
主键等值更新: RC:行锁 RR:行锁
唯一索引等值更新:RC:行锁 RR:行锁
非唯一索引等值更新:RC:行锁 RR:临键锁
范围更新:RC:仅命中行加行锁 RR:临键锁+间隙锁
无索引更新:RC:全表行锁(逐行检查) RR:全表行锁+间隙锁
唯一性检查:RC:短暂S锁 RR:可能加间隙锁
RR隔离级别的情况下,插入数据都会涉及什么锁:
- 表级意向排他锁(IX Lock)
事务开始时,先对表加 IX 锁(Intention Exclusive Lock)
作用:表明"该事务打算修改表中的某些行"
特点:不阻塞其他事务的读操作(如 SELECT … LOCK IN SHARE MODE) - 唯一性检查锁(S Lock)
检查主键/唯一键冲突时,对冲突行或间隙加锁:
若存在冲突行 → 加 S 锁(共享锁) 在冲突行上
若不存在冲突行 → 加 间隙锁(Gap Lock) 在目标间隙
示例:
INSERT INTO users (id, name) VALUES (15, 'Alice');
-- 若 id=15 已存在:对 id=15 行加 S 锁
-- 若 id=15 不存在:在 (10,20) 间隙加 Gap Lock(假设 id=10 和 id=20 存在)
- 插入意向锁(Insert Intention Lock)
在目标插入位置加 插入意向锁(特殊的间隙锁)
作用:表明"事务准备在此间隙插入新记录"
特点:
允许多个事务在同一间隙的不同位置并发插入
与已存在的 Gap Lock/Next-Key Lock 互斥(可能被阻塞) - 行级排他锁(X Lock)
插入成功后,对新插入的行加 X 锁(排他锁)
锁持续到事务结束(确保事务内数据一致性)
RR隔离级别,不同场景下的锁行为
场景 1:无冲突插入
INSERT INTO orders (order_id, amount) VALUES (1001, 99.9);
加锁流程:
- 表级 IX 锁
- 检查 order_id=1001 无冲突 → 在目标间隙加 插入意向锁(如 order_id 在 (1000,1005) 之间)
- 插入成功后对新行 order_id=1001 加 X 锁
场景 2:唯一键冲突
-- 假设 order_id=1001 已存在
INSERT INTO orders (order_id, amount) VALUES (1001, 88.8);
加锁流程:
- 表级 IX 锁
- 发现 order_id=1001 存在 → 对冲突行加 S 锁
- 抛出 Duplicate entry 错误,事务回滚释放锁
场景 3:被间隙锁阻塞
-- 事务1(先执行)
SELECT * FROM orders WHERE order_id BETWEEN 1000 AND 1005 FOR UPDATE;
-- 对 (1000,1005] 加临键锁(Next-Key Lock)
-- 事务2(后执行)
INSERT INTO orders (order_id, amount) VALUES (1002, 50.0);
被事务1的临键锁阻塞(插入意向锁与临键锁互斥)
三、自增主键的特殊锁
若表有自增主键(AUTO_INCREMENT),还会加 自增锁(AUTO-INC Lock):
CREATE TABLE logs (
id INT AUTO_INCREMENT PRIMARY KEY,
content TEXT
);
INSERT INTO logs (content) VALUES ('log1'); -- 加自增锁
锁行为:
- 简单插入(值确定):加轻量级锁(申请自增值后立即释放)
- 批量插入(如 INSERT … SELECT):加表级自增锁直到语句结束
RC的隔离级别下,插入一条数据会添加什么锁?
- 表级意向排他锁(IX Lock)
事务开始时对表加 IX 锁(Intention Exclusive Lock)
作用:表明事务打算修改表中的某些行
特点:不阻塞其他事务的读操作 - 唯一性检查锁(短暂 S 锁)
检查主键/唯一键冲突时:
若目标行存在 → 对冲突行加 S 锁(共享锁)
若目标行不存在 → 不加间隙锁(与 RR 级别的关键区别)
锁立即释放:检查完成后立即释放 S 锁(不等待事务结束) - 插入意向锁(Insert Intention Lock)
在目标插入位置加插入意向锁
特点:
允许多个事务在同一间隙并发插入
仅与已存在的 Gap Lock 互斥(但在 RC 级别几乎没有 Gap Lock) - 行级排他锁(X Lock)
插入成功后,对新插入的行加 X 锁(排他锁)
锁持续到事务结束(COMMIT/ROLLBACK 时释放)
RC和RR锁机制的区别:
间隙锁 (Gap Lock): RC: 几乎不使用 RR: 大量使用
唯一性检查未命中: RC: 不加锁 RR: 加间隙锁
插入意向锁冲突: RC: 只与现有 Gap Lock 互斥 RR: 与 Gap Lock/Next-Key Lock 互斥
幻读 (Phantom Read):RC:允许发生 RR:通过锁机制防止