1.概述
InnoDB支持多粒度锁定,允许行锁和表锁共存。意向锁是在表级别上设置的锁,预示着事务稍后可能会在表中的某行上设置哪种类型的锁(共享锁 or 排他锁)。
意向锁有两种主要类型:
- 意向共享锁(Intention Shared Lock,简称 IS):表明一个事务打算对表中的某一行设置共享锁。
- 意向排它锁(Intention Exclusive Lock,简称 IX) :表明一个事务打算对表中的某一行设置排他锁。
例如,对表中的一个数据行执行 SELECT ... FOR SHARE 语句,会设置一个 IS 锁。执行 SELECT ... FOR UPDATE 会设置一个 IX 锁。下面是锁兼容性的矩阵:
X | IX | S | IS | |
X | Conflict | Conflict | Conflict | Conflict |
IX | Conflict | Compatible | Conflict | Compatible |
S | Conflict | Conflict | Compatible | Compatible |
IS | Conflict | Compatible | Compatible | Compatible |
意向锁并不会阻止任何全表请求(例如,LOCK TABLES … WRITE)。意向锁的主要目的是显示有人正在锁定一行,或者准备锁定一行。在"SHOW ENGINE INNODB STATUS"的输出中,意向锁的事务数据可能看起来像这样:
TABLE LOCK table `test`.`t` trx id 10080 lock mode IX
实际上,如果不使用意向锁,要确定表级锁和行级锁之间的冲突,可能需要遍历所有行的行锁。有了意向锁,我们只需要检查是否存在意向锁,就可以确定是否存在行锁。
2.实验部分
我们先来搭建一个MySQL测试环境,这儿提供一个Docker Compose Yaml文件来快速启动MySQL。对于Windows用户来说,我还是推荐DockerDesktop, 当然Podman也是可以的。
MySQL Docker Compose Yaml文件如下,文件保存为`mysql8.yaml`:
version: '3.8'
services:
mysql:
image: mysql:8.0
command: --default-authentication-plugin=mysql_native_password
environment:
MYSQL_ROOT_PASSWORD: 12345679
MYSQL_DATABASE: test
MYSQL_USER: myuser
MYSQL_PASSWORD: userpassword
ports:
- '3306:3306'
volumes:
- my-db:/var/lib/mysql
restart: always
volumes:
my-db:
启动MySQL
docker-compose -f .\mysql8.yaml up -d
接下来我们来进行我们的实验。
首先,我们创建测试表 sys_user:
CREATE TABLE `sys_user` (
`id` int NOT NULL,
`name_pinyin` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
insert into `sys_user` (id, name_pinyin) values (17, 'xiaosi');
2.1 冲突实验
然后,我们进行以下操作来测试锁的冲突:
第一步,事务A获取IS锁,代码如下:
# Transaction A
BEGIN;
SELECT * FROM sys_user WHERE id = 17 lock in share mode;
第二步,事务B尝试获取IX锁,代码如下:
# Transaction B
BEGIN;
SELECT * FROM sys_user WHERE id = 17 FOR UPDATE;
执行以上两步,你会发现事务B阻塞,这是因为IX锁与IS锁冲突。在经过一段时间后事务会因为超时回滚。
事务B被阻塞:
事务B超时回滚
2.2 兼容测试
接下来,我们测试一些兼容的锁类型。
第一步,事务A获取IS锁,代码如下:
# Transaction A
BEGIN;
SELECT * FROM sys_user WHERE id = 17 lock in share mode;
第二步,事务B尝试获取S锁,代码如下:
# Transaction B
BEGIN;
SELECT * FROM sys_user WHERE id = 17 lock in share mode;
执行以上两步,你会发现事务B没有阻塞,这是因为S锁与IS锁兼容。
事务A和事务B正常执行未被阻塞:
2.3 锁结果查询截图
3. 总结
希望能在面试中帮到你。