行级锁每次操作均会锁定对应的行数据,锁定粒度最小,致使锁冲突发生的可能性最低,因而并发度最高。它被应用于 InnoDB 存储引擎之中,要知道,InnoDB 的数据是依据索引来组织的,行锁通过对索引上的索引项加锁来实现,并非针对记录加锁。
同时,InnoDB 行锁包含共享锁(S)和排他锁(X) ,而行锁的共享锁和排他锁还能够继续细分成为三类:记录锁、间隙锁与临键锁。
记录锁(Record Lock)
作用:锁定表中的单行记录,防止其他事务对其进行update或delete。
情况:当执行精确匹配查询(例如
WHERE id = 1
)并且查询的索引是唯一索引时,会对匹配的行加记录锁。示例:假设有表
students
,其中id
是主键,事务 A 执行SELECT * FROM students WHERE id = 15 FOR UPDATE;
,此时就会对id = 15
的这行数据加记录锁。图示:
间隙锁(Gap Lock)
作用:锁定一个范围,但不包含记录本身,防止其他事务在这个间隙上insert,主要用于防止幻读。
情况:在可重复读隔离级别下,对于范围查询(如
WHERE id > 5
),如果查询的索引不是唯一索引,会使用间隙锁。示例:事务 B 执行
SELECT * FROM students WHERE age > 20 FOR UPDATE;
,假设age
列上没有唯一索引,此时会对age
值大于 20 的间隙加间隙锁。图示:
临键锁(Next-Key Lock)
作用:是记录锁和间隙锁的组合,既锁住记录,又锁住记录前面的间隙。
情况:在可重复读隔离级别下,当使用范围查询且查询的索引是唯一索引时,会使用临键锁。
示例:事务 C 执行
SELECT * FROM students WHERE id >= 20 FOR UPDATE;
,如果id
是唯一索引,会对id >= 20
的范围加临键锁。图示:
行锁
InnoDB的行锁包含共享锁(S)和排他锁(X),在实现方式上表现为记录锁、间隙锁、临键锁。
共享锁(S 锁):允许其他事务同时读取被锁定的数据,但不允许修改。
排他锁(X 锁):则具有排他性,不允许其他事务获取共享锁或排他锁来访问被锁定的数据。
两种锁的兼容情况如下:
锁类型 | 共享锁(S 锁) | 排他锁(X 锁) |
---|---|---|
共享锁(S 锁) | 兼容(✔) | 冲突(×) |
排他锁(X 锁) | 冲突(×) | 冲突(×) |
常见的SQL,加锁情况:
SQL | 行锁类型 | 加锁情况 |
---|---|---|
SELECT | 无锁 | 没有任何加锁 |
SELECT ... LOCK IN SHARE MODE | 共享锁(S 锁) | 需要在SELECT 查询末尾加上‘LOCK IN SHARE MODE’ |
SELECT ... FOR UPDATE | 排他锁(X 锁) | 需要在SELECT 查询末尾加上‘FOR UPDATE’ |
INSERT | 排他锁(X 锁) | 自动会加上排他锁 |
UPDATE | 排他锁(X 锁) | 自动会加上排他锁 |
DELETE | 排他锁(X 锁) | 自动会加上排他锁 |
总结
总之,InnoDB 丰富多样的行锁机制,从共享锁与排他锁的大分类,到记录锁、间隙锁和临键锁的具体形式,为数据库的事务处理和并发操作提供了精细且可靠的控制手段。熟悉并驾驭这些行锁,是实现高性能、稳定可靠的数据库系统的重要基石。