【mysql】锁机制 - 3.意向锁
意向锁(Intension Lock)
是为了提高粗粒度锁性能而设置的一种预判机制,即在一个操作发起实际资源的锁申请行为之前,先对更粗力度的资源发起一个加锁意向声明。
为什么需要意向锁?
比如对于以下操作:
事务1 | 事务2 | |
Begin | Begin | |
Time1 | SELECT * FROM test1 WHERE id = 1000 FOR UPDATE; | |
Time2 | LOCK TABLES test1 READ; |
此时事务2 想要获得 test1 表的共享锁,需要保证:
-
没有其他事务持有 test1 表的排他锁
-
没有其他事务出游 test1 表中任意一行的排他锁
为了检测是否满足第2个条件,必须逐行扫描 test1 表是否存在行排他锁,显然这效率很差
意向锁机制
为了优化上述问题,mysql 引入了意向锁机制。意向锁是一种表锁,不与行级锁冲突,行级锁和表级锁可以共存。
意向锁分为两种:
-
意向共享锁(IS):有意向对表中的一些行加 S 锁
-
意向排他锁(IX):有意向对表中的一些行加 X 锁
意向锁之间的兼容性关系:
意向共享锁(IS) | 意向排他锁(IX) | |
意向共享锁(IS) | 兼容 | 兼容 |
意向排他锁(IX) | 兼容 | 兼容 |
意向锁和普通锁的兼容性关系:
意向共享锁(IS) | 意向排他锁(IX) | |
表级共享锁(S) | 兼容 | 互斥 |
表级排他锁(X) | 互斥 | 互斥 |
回到刚刚的例子:
事务1 | 事务2 | 事务3 | |
Begin | Begin | Begin | |
Time1 | SELECT * FROM test1 WHERE id = 1000 FOR UPDATE; (获得IX锁,获得第1000行的X锁) | ||
Time2 | LOCK TABLES test1 READ; (获得IS锁,等待表级S锁) | SELECT * FROM test1 WHERE id = 5 FOR UPDATE; (获得IX锁,获得第5行的X锁) |
这时 test1 表存在两把锁:test1 表上的意向排他锁,id = 1000 数据行上的排他锁
事务2 会申请:
- test1 表上的意向排他锁——检测到 test1 表上已存在其他的意向排他锁,但是意向锁和意向锁之间并不互斥,所以可以获得意向排他锁
- test1 表上的共享锁——检测到 test1 表已经存在其他的意向排他锁,加锁请求直接被阻塞
事务3会申请:
-
test1 表上的意向排他锁——检测到 test1 表上已存在其他的意向排他锁,但是意向锁和意向锁之间并不互斥,所以可以获得意向排他锁
-
id = 5 数据行上的排他锁——由于此行上没有其他的排他锁(意向锁不会和行级锁冲突),所以可以获得排他锁
参考:
MySQL :: MySQL 8.0 Reference Manual :: 17.7.1 InnoDB Locking
https://juejin.cn/post/6844903666332368909