使用select ...for update查询时是会索表还是会锁行的问题,之前没注意过这个问题,以为会锁行。不够经过验证后和自己所想的还是有较大出入,要看它加的是行锁或者表锁,需要看sql在执行的过程中是不是用了主键或索引,如果用了主键或索引,加的就是行锁,否则就是表锁。

我用的mysql5.7,先创建一张用户表t_user有三个字段的表,分别是u_id、u_name、t_num三个字段,其中u_id为主键自增序列,为u_num为字段建立索引。建表语句及用到的sql如下:

//建表sql create table if not exists t_org_user( u_id bigint(30) not null auto_increment, u_num varchar(255) not null default '', u_name varchar(255) not null default '', primary key(id), key(u_num) using btree ) engine=innodb auto_increment=1 default charset=utf8mb4; //插入数据sql insert into t_user(u_num,u_name) values('1','张三'); insert into t_user(u_num,u_name) values('2','赵四'); insert into t_user(u_num,u_name) values('3','王五'); insert into t_user(u_num,u_name) values('6','赵六'); //查看表结构sql show create table t_org_user \G; //检查sql是否走索引 explain select * from t_org_user where id = 1; //检查事务是否自动提交sql show variables like '%commit%'; //设置事务手动提交 set autocommit = 0; //手动加悲观锁 select * from t_org_user where id = 1 for update; //更新字段sql update t_org_user set name = '李四' where number = '002'; 验证之前先插入几条数据,执行上述sql中的insert语句。MySQL事务默认都是自动提交的,在做这种场景验证的时候,需要将自动提交事务关闭掉改为手动提交,对应的sql命令为set autocommit = 0。该指令是会话级别的,也就是如果重新开一个mysql命令行窗口就要执行set autocommit =0指令,从而将事务由自动提交改为手动提交。开启两个mysql命令行窗口做场景演示,假设分别命名为窗口1和窗口2.

场景一:验证走主键约束是锁表还是锁行

验证场景之前首先要把对应的会话的事务自动提交关闭掉。

 1.在窗口1中执行sql: select * from t_org_user where id = 1 for update;

 2.在窗口2中执行sql:  update t_org_user set name = '张三1' where id = 1;失败,提示”Lock wait timeout exceeded; try restarting transaction“错误。

 3.在窗口2中先将事务回滚掉再执行sql:update t_org_user set name = '李四2' where id = 2;执行成功

 通过场景1的验证,我们可以得出结论,如果是走主键索引,加的是行级锁。
场景二:验证走约束是锁表还是锁行

验证场景之前首先要把对应的会话的事务自动提交关闭掉。

 1.在窗口1中执行sql: select * from t_org_user where number = '001' for update;

 2.在窗口2中执行sql:  update t_org_user set name = '张三1' where id = 1;失败,提示”Lock wait timeout exceeded; try restarting transaction“错误。

 3.在窗口2中先将事务回滚掉再执行sql: update t_org_user set name = '李四' where id = 2;执行成功

     通过场景2的验证,我们可以得出结论,如果是走的是约束索引,加的是行级锁。
场景三:验证不走主键约束或普通约束是锁表还是锁行

验证场景之前首先要把对应的会话的事务自动提交关闭掉。

 1.在窗口1中执行sql: select * from t_org_user where name = '张三' for update;

 2.在窗口2中执行sql:update t_org_user set name = '张三1' where id = 1;失败,提示”Lock wait timeout exceeded; try restarting transaction“错误。

 3.在窗口2中先将事务回滚掉再执行sql:update t_org_user set name = '李四2' where id = 2;执行提示”Lock wait timeout exceeded; try restarting transaction“错误。

 4.在窗口2中先将事务回滚掉再执行sql:insert into t_org_user        (number,name)values('005','洪七');执行失败。提示”Lock wait timeout exceeded; try restarting transaction“错误。

通过场景三的验证,我们可以得出结论,如果是不走主键或普通约束,加的是表级锁。

mysql中锁和加锁机制