我们先介绍下意向锁的作用,mysql官网有这么一段介绍
The main purpose of IX and IS locks is to show that someone is locking a row, or going to lock a row in the table.
加意向锁的目的是为了表明某个事务正在锁定一行或者将要锁定一行。表名加锁的“意图”。
要读写,直接加S锁或者X锁就好啦,为什么还要表名这样一个意图呢?
意向锁有两种:
- 意向共享锁(IS)表示事务意图在表中的单个行上设置共享锁。
- 意向排他锁(IX)表明事务意图在表中的单个行上设置独占锁。
我们先了解一下意向锁是在什么时候使用的。
- 在一个事务对一张表的某行添加S锁之前,它必须对该表获取一个IS锁或者优先级更高的锁。
- 在一个事务对一张表的某行添加X锁之前,它必须对该表获取一个IX锁。
那么为什么要这么做呢?这么做有什么好处呢?
这里就需要介绍一下表锁了。
以前我们说X锁或者S锁,都是在说给一行加上X锁或者S锁,如果我们用select * from student for update,是会student表加上X锁的。
数据库里面是同时允许锁表,或者锁行的。如果事务A需要修改某一行数据,则他会给该行加X锁。
这时事务B想申请整个表的X锁做某些操作。他能否申请成功呢?不能,因为申请成功则代表事务B可以对任意行做读写。显然这与事务A冲突了。
那这时数据库应该怎么判断呢?
可行方案就是,逐行判断是否加了X锁,如果都没加,就代表可以锁表,如果其中某一行加上了X锁,那么就不能整表加锁,不过,显然这样效率实在是太低了......
这个时候来看意向锁,就可以看出他发挥了重要的重要:
事务A想对某行上X锁之前,必须要获得到表的IX锁,现在没有其他事务使用IX锁,所以事务A获取成功。
事务A——获得IX锁——对某行上X锁
这个时候事务B想对这个表上X锁,发现IX表已经被拿走了,证明目前有其他事务正在修改该表的某行或者多行,此时事务B被阻塞......
IX,IS是表级锁,不会和行级的X,S锁发生冲突。只会和表级的X,S发生冲突
意向锁之间是相互兼容的:
- | 意向共享锁(IS) | 意向排他锁(IX) |
---|---|---|
意向共享锁(IS) | 兼容 | 兼容 |
意向排他锁(IX) | 兼容 | 兼容 |
为什么都是兼容呢?
事务A加了表的IX锁,或者IS锁,只代表事务A已锁定一行或者将要锁定一行。事务B当然也可以锁定其他的行,所以事务B肯定也是可以获得表的IS锁或者IX锁的。
- | 意向共享锁(IS) | 意向排他锁(IX) |
---|---|---|
共享锁(S) | 兼容 | 冲突 |
排他锁(X) | 冲突 | 冲突 |
再次重申:这里的S锁和X锁是表级别的,意向锁不会与行级别的S锁和X锁冲突
举个例子:
- 事务A已经获得了IS锁,想要读取某行数据,事务B想要获得表的S锁,可以获得成功,因为读是兼容的
- 事务B获得了表的IX锁,想要修改某行数据,事务B想要获得表的X锁,但是由于IX锁已被获取走,证明有其他事务正在修改某行数据,所以事务B获得失败,只能被组塞住...
总结:
- InnoDB 支持多粒度锁,特定场景下,行级锁可以与表级锁共存。
- 意向锁之间互不排斥,但除了 IS 与 S 兼容外,意向锁会与 共享锁 / 排他锁 互斥。
- IX,IS是表级锁,不会和行级的X,S锁发生冲突。只会和表级的X,S发生冲突。
- 意向锁在保证并发性的前提下,实现了行锁和表锁共存且满足事务隔离性的要求。