「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」
1.定义
select ... for update;
官方文档给出定义: 读最新的可见数据,并为读取的行设置独占锁。
2.结论
使用select...for update;语句时,
如果查询条件是明确的主键/索引条件时,行锁;否则,表锁;
3.实操
首先我们创建一个简单的表:
create table if not exists `user`(
`id` bigint unsigned auto_increment,
`name` varchar(20) not null,
`password` varchar(20) not null,
primary key (`id`)
) ENGINE=InnoDB default charset=utf8;
并且创建了主键id
向这张表中插入2条数据:
查看mysql的自动提交配置:
select @@autocommit;
关闭自动提交:
set @@autocommit = 0;
我们再开一个终端窗口:
这时候查看mysql自动提交配置发现还是开着的,重复上述操作,关闭自动提交。
我们在第一个窗口中执行以下命令:
begin;
select * from user where id = 1 for update;
可以看到根据主键id进行准确查询,此时不提交当前事务。
我们在第二个窗口进行查询:
可以看到,不管是全表查询还是条件查询都可以正常进行。
我们尝试修改下id为2的数据:
可以看到成功执行了,我们对id为1的数据再次执行select ... for update;
这时候会发现,这条语句阻塞在这里了,过一会就提示超时
那我们修改id为1的数据呢?
同样的结果 提交第一个窗口的事务;
可以看到第二个窗口的命令执行成功了:
由此我们可以得出第一个结论(索引方式此处不贴了):
使用select ... for update; 查询条件为主键/索引时,发生行锁。
我们再次尝试第二种情况,第一个窗口中执行非主键/索引的查询条件语句:
在第二个窗口尝试更新id为2的数据:
结果是阻塞了。
并且此时执行select ... for update也是阻塞的。
窗口1事务提交后,窗口2可以正常执行更新语句。
由此,得出第二个结论:
使用select ... for update; 查询条件为非主键/索引时,发生表锁。
那如果是主键/索引范围查询呢?
我们给出的条件是对id>1的数据进行锁定查询,在另一个事务中,可以看到对不满足条件的语句是可以正常执行select ... for update;和update语句的,但是对于id>1数据,就被锁住了。
那非主键/索引的范围查询呢?
我把数据改成了下面这样:
对name模糊匹配的china的数据进行查询锁定:
可以看到不管是范围内的还是范围外的都被锁住了,即表锁: