
一、什么是意向锁?
在MySQL的InnoDB存储引擎中,锁的机制非常丰富。当我们谈论意向锁(Intention Locks)时,首先要明确一个核心概念:意向锁是表级锁。
意向锁的主要作用是表明事务打算对表中的某一行(或某些行)加什么类型的行锁。它本身不会锁定具体的行,只是作为一个“路标”存在。意向锁分为两种:
意向共享锁(IS Lock):事务打算给数据行加行共享锁(S锁),在给行加S锁之前,必须先取得该表的IS锁。
意向排他锁(IX Lock):事务打算给数据行加行排他锁(X锁),在给行加X锁之前,必须先取得该表的IX锁。
二、为什么需要意向锁?
如果没有意向锁,当我们要给一张表加表级锁(比如LOCK TABLES ... WRITE)时,数据库引擎必须遍历整张表的所有行,检查是否有行锁与即将添加的表锁冲突。这在数据量大的情况下,效率极其低下。
有了意向锁,这个过程就变成了“看标记”的机制:
事务A想修改某一行,它会先给这张表加上意向排他锁(IX),然后再给这一行加行排他锁(X)。
事务B想给整张表加表级排他锁(X),它不需要遍历全表,只需要检查这张表上是否存在意向锁(IX)。如果存在IX锁,说明表内肯定有行正在被修改,事务B的表锁申请就会被阻塞。
简而言之,意向锁极大提升了表锁与行锁之间冲突检测的效率,它是一个自动维护的状态标志。
三、意向锁的兼容性规则
意向锁之间是互相兼容的,因为它们只是表达了一种“意图”,多个事务可以同时有修改不同行的意图。意向锁只与表级锁冲突。具体的兼容矩阵如下:
| 当前持有锁 请求锁 | IS | IX | S(表锁) | X(表锁) |
|---|---|---|---|---|
| IS | 兼容 | 兼容 | 兼容 | 冲突 |
| IX | 兼容 | 兼容 | 冲突 | 冲突 |
| S(表锁) | 兼容 | 冲突 | 兼容 | 冲突 |
| X(表锁) | 冲突 | 冲突 | 冲突 | 冲突 |
重点记忆:IX锁与表级S锁冲突,IS锁与表级X锁冲突,IX锁与表级X锁冲突。行级锁与表级意向锁永远是兼容的。
四、实战演示:如何观察意向锁
在MySQL 8.0及以上版本,我们可以通过performance_schema.data_locks表来清晰地观察意向锁的产生过程。如果你需要建表测试数据,可以访问 www.ipipp.com 获取相关的测试脚本。
步骤1:在会话A中开启事务并锁定一行
BEGIN; SELECT * FROM test_user WHERE id = 1 FOR UPDATE;
此时,事务A不仅给id=1的行加了排他锁(X),还自动给test_user表加了意向排他锁(IX)。
步骤2:在会话B中查看当前的锁情况
SELECT OBJECT_NAME, LOCK_TYPE, LOCK_MODE FROM performance_schema.data_locks WHERE OBJECT_SCHEMA = 'test_db';
在查询结果中,你会看到两条关键的锁记录:
LOCK_TYPE为TABLE,LOCK_MODE为IX(这就是自动生成的意向排他锁)
LOCK_TYPE为RECORD,LOCK_MODE为X,REC_NOT_GAP(这就是行级排他锁)
步骤3:验证意向锁与表锁的冲突
在会话C中尝试获取表级共享锁:
LOCK TABLES test_user READ;
由于会话A持有表的IX锁,而IX锁与表级S锁是冲突的,因此会话C会被阻塞,直到会话A执行COMMIT或ROLLBACK释放IX锁为止。
五、总结
意向锁是InnoDB自动维护的表级锁,开发者无法手动添加或解除。它的核心价值在于作为行锁和表锁之间的通信桥梁,通过空间换时间的策略,避免了全表扫描来检测行锁冲突的问题。理解意向锁的机制,能够帮助我们更好地分析数据库死锁和锁等待问题,是掌握MySQL并发控制机制的重要一步。