一、意向锁是什么?
意向锁(Intention Lock) 是InnoDB中的一种表级锁,用来"声明"某个事务即将在表中的某些行上获取行锁。
简单理解:它是一种预告或标记,告诉其他事务:"我要操作这个表里的某些行了,你们注意点!"
二、为什么要用意向锁?
没有意向锁的问题
假设:
- 事务A要修改表中的某一行(需要行锁)
- 事务B要给整个表加表锁(比如ALTER TABLE)
如果没有意向锁:
事务B需要检查表中的
每一行是否被锁
如果有100万行,就要检查100万次!
性能极差!
有意向锁的解决方案
意向锁相当于一个快捷标记:
- 事务A:在操作行之前,先在表上加意向锁(瞬间完成)
- 事务B:检查表上是否有意向锁(只需检查1次)
- 如果有意向锁,说明有其他事务在操作行,就不能加表锁
三、意向锁的类型
1. 意向共享锁(IS)
-- 事务A:准备读取某些行
SELECT * FROM users WHERE id = 1 LOCK IN SHARE MODE;
-- 步骤:
-- 1. 先在表users上加意向共享锁(IS)
-- 2. 再在id=1的行上加共享锁(S)
2. 意向排他锁(IX)
-- 事务A:准备修改某些行
UPDATE users SET name = '张三' WHERE id = 1;
-- 步骤:
-- 1. 先在表users上加意向排他锁(IX)
-- 2. 再在id=1的行上加排他锁(X)
四、锁的兼容性矩阵
| 当前锁模式 |
IS |
IX |
S |
X |
|---|
| IS |
✅ |
✅ |
✅ |
❌ |
| IX |
✅ |
✅ |
❌ |
❌ |
| S |
✅ |
❌ |
✅ |
❌ |
| X |
❌ |
❌ |
❌ |
❌ |
✅ = 兼容(可以共存)
❌ = 冲突(不能共存)
五、实际例子理解
场景1:多个事务读不同行
-- 事务A(读取id=1)
BEGIN;
SELECT * FROM users WHERE id = 1 LOCK IN SHARE MODE;
-- 加:表级IS锁 + 行级S锁
-- 事务B(读取id=2)
BEGIN;
SELECT * FROM users WHERE id = 2 LOCK IN SHARE MODE;
-- 加:表级IS锁 + 行级S锁
-- ✅ 兼容!因为IS锁之间兼容
场景2:读和写冲突
-- 事务A(修改id=1)
BEGIN;
UPDATE users SET name = 'A' WHERE id = 1;
-- 加:表级IX锁 + 行级X锁
-- 事务B(想读整个表)
BEGIN;
LOCK TABLES users READ; -- 尝试加表级S锁
-- ❌ 等待!因为IX和S不兼容
场景3:两个写不冲突的行
-- 事务A(修改id=1)
BEGIN;
UPDATE users SET name = 'A' WHERE id = 1;
-- 加:表级IX锁 + 行级X锁(id=1)
-- 事务B(修改id=2)
BEGIN;
UPDATE users SET name = 'B' WHERE id = 2;
-- 加:表级IX锁 + 行级X锁(id=2)
-- ✅ 兼容!因为IX锁之间兼容,且操作不同行
六、如何查看意向锁?
-- 查看当前锁信息
SELECT * FROM information_schema.INNODB_LOCKS;
SELECT * FROM information_schema.INNODB_LOCK_WAITS;
-- 或者使用
SHOW ENGINE INNODB STATUS\G;
-- 在输出中查找 "LOCK GRANT" 部分
七、重要总结
| 特性 |
说明 |
|---|
| 表级锁 |
意向锁是加在表上的,不是行 |
| 快速检查 |
避免全表扫描检查行锁 |
| 两种类型 |
IS(意向共享)、IX(意向排他) |
| 兼容性 |
IS和IX之间可以共存 |
| 自动管理 |
MySQL自动加意向锁,无需手动操作 |
八、常见面试题
Q:意向锁会阻塞其他事务吗?
A:意向锁之间不互斥(IS和IX兼容),但意向锁和某些表锁互斥。
Q:意向锁会影响性能吗?
A:基本不影响。它实际上提升性能,避免了全表扫描检查行锁。
Q:所有存储引擎都有意向锁吗?
A:不是!只有InnoDB有,MyISAM没有意向锁。
九、一句话记住意向锁
"意向锁是表的门卫,它不负责具体安保(行锁负责),只告诉访客:里面有人正在办事(IS/IX),你们先等等!"
理解意向锁,你就掌握了InnoDB锁机制的50%!它让表锁和行锁能够高效共存。