意向锁介绍
意向锁是一种特殊的表级锁,它并不会直接锁定数据行,而是用来表明一个事务打算在表中的某些行上设置共享锁或排他锁。 我们可以将其理解为一个“信号”或“标识”,当这个信号存在时,其他事务就能快速地了解到表内已经存在或即将存在行锁,而无需逐行检查。
意向锁由数据库引擎(如MySQL的InnoDB)自动管理和维护,用户无法也无需手动进行操作。 当事务需要给某一行数据加锁时,数据库会自动先为该行所在的表加上相应的意向锁。
意向锁主要分为以下两种类型:
- 意向共享锁 (Intention Shared Lock, IS): 表示一个事务准备或者已经对表中的某些行设置了共享锁(S锁,也称读锁)。例如,执行
SELECT ... FOR SHARE语句前,会先在表上加一个IS锁。 - 意向排他锁 (Intention Exclusive Lock, IX): 表示一个事务准备或者已经对表中的某些行设置了排他锁(X锁,也称写锁)。例如,执行
UPDATE,DELETE,INSERT或SELECT ... FOR UPDATE语句前,会先在表上加一个IX锁。
为什么需要意向锁?
意向锁的出现主要是为了解决一个效率问题:如何在存在行锁的情况下,高效地为整个表加锁?
想象一个没有意向锁的场景:
- 事务A 对表中的某一行记录加上了排他锁(行锁)。
- 此时,事务B 想要对整个表加上表级排他锁(例如执行
LOCK TABLES ... WRITE;)。 - 为了确保加锁的安全性,事务B必须确认表里没有任何一行被其他事务锁住。
- 因此,事务B不得不遍历表中的每一行,去检查是否存在与它的表锁相冲突的行锁。
如果这张表的数据量非常大(比如有数百万行),逐行扫描的开销将是巨大的,这会严重影响数据库的性能。
引入意向锁后的流程如下:
- 事务A 在给某一行加排他锁(行锁)之前,会先自动地为该表加上一个意向排他锁(IX锁)。
- 当事务B 想要对整个表加表级排他锁时,它不再需要逐行扫描。
- 事务B会首先检查该表是否存在意向锁。它发现表上已经存在一个IX锁。
- IX锁的存在告诉事务B:“表里肯定有某些行被加了排他锁”。由于表级排他锁与行级排他锁是冲突的,事务B的加锁请求就会被阻塞,直到事务A释放它的行锁(和意向锁)。
通过这种方式,数据库仅需一次表级别的检查,就能判断是否可以安全地授予表锁,从而避免了代价高昂的全表行锁扫描,极大地提升了加锁判断的效率。
意向锁的兼容性
理解意向锁的关键在于它的兼容性规则。意向锁之间是互相兼容的,也就是说,一个事务获取了IS锁,另一个事务仍然可以获取IS或IX锁。 它们的主要作用是与表级的共享锁(S)和排他锁(X)产生冲突。
| 表锁 X (排他) | 表锁 S (共享) | 意向锁 IX | 意向锁 IS | |
|---|---|---|---|---|
| 表锁 X (排他) | 冲突 | 冲突 | 冲突 | 冲突 |
| 表锁 S (共享) | 冲突 | 兼容 | 冲突 | 兼容 |
| 意向锁 IX | 冲突 | 冲突 | 兼容 | 兼容 |
| 意向锁 IS | 冲突 | 兼容 | 兼容 | 兼容 |