简介:for update排他锁级别
实现锁的方式有排他锁和共享锁,在数据更新时我们往往需要使用排他锁将某条数据锁住,防止其他进程读取导致脏读。
在之前的文章【Mysql锁机制及测试】中已经讲过关于共享锁和排他锁的测试,有兴趣的可以去看看
今天我们就来说说实现排他锁的 for update 语句,锁的级别又是什么呢?是锁表吗?
准备工作
创建测试表并添加测试数据,默认只有主键索引
// 测试表
create table `user`(
`id` int unsigned primary key auto_increment,
`nickname` varchar(100) not null default '' comment '昵称',
`integral` int unsigned not null default 0 comment '剩余积分',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP comment '创建时间',
`login_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP comment '最后登录时间',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP comment '最后更新时间'
)engine=innodb charset=utf8 comment '用户表';
//测试数据
insert into user(nickname,integral,create_time,login_time,update_time) values('测试一',1,'2023-2-1 14:10:10','2023-2-1 14:10:10','2023-2-1 14:10:10'),
('测试二',2,'2023-3-1 14:10:10','2023-3-1 14:10:10','2023-3-1 14:10:10'),('测试二',2,'2023-4-1 14:10:10','2023-4-1 14:10:10','2023-4-1 14:10:10');
表中create_time、login_time及update_time字段非必要字段,大家可以删除
测试没有使用索引查询,锁的级别
1.开启事务,使用排他锁(for update)查询某一条数据。(注意此处不要提交事务,也不要关闭MySQL命令行)
//开启事务
begin;
//查询昵称为测试一的用户,此处不提交事务
select * from user where nickname='测试一' for update;
2.重新开启一个命令窗口进入MySQL,查询user表的数据或者更新其他数据
update user set integral=2 where nickname='测试二';
此时,我们会发现MySQL一直再等待中,无法查询数据。哪怕是更新其他数据也不行,此时锁住的是整个表。
注意:在命令行使用select查询会忽略锁,直接读取。但是在PHP代码又可以实现加锁效果
测试使用索引或者主键查询时for update 锁级别
1.开启事务,使用排他锁(for update)查询某一条数据。(注意此处不要提交事务,也不要关闭MySQL命令行)
//开启事务
begin;
//查询id为1的用户,此处不提交事务
select * from user where id=1 for update;
2.重新开启一个命令窗口进入MySQL,查询user表的数据或者更新其他数据
// 更新id为1的用户的积分
update user set integral=3 where id=1;
// 更新id为2的用户的积分
update user set integral=3 where id=2;
此时我们会发现更新id为1的用户积分时,sql一直处于等待状态,直到我们在上一个MySQL窗口提交事务后才更新成功。但是更新id为2的用户积分时却不受影响,由此可见,当查询条件包含主键时 for update 是行级锁
当然我们也可以在查询的字段上创建索引,例如,我们在昵称字段上创建索引
alter table user add key nickname(nickname);
然后再使用for update 去加锁时,除了where条件锁住的行不能查询更新外,其他的数据依然可以查询更新。
由此我们得出结论:
如果查询条件使用索引或主键,for update 是行级锁。
如果查询条件没有使用索引或主键,for update 是表级锁
有遗漏或者不对的可以在我的公众号留言哦