文章目录
- 一、for update 是什么?
- B站视频地址:【数据库 for update 详细教程(行锁还是表锁问题演示)-哔哩哔哩】 [https://b23.tv/4XToMlN](https://b23.tv/4XToMlN)
- 二、我们通常什么情况下会用到它?
- 1 .在我们的实际业务场景中,有些情况并发量不太高,为了保证数据的正确性,使用悲观锁也可以
- 三、select……for update会锁表还是锁行?
- 1.有主键的情况下
- 2.有普通索引或者唯一索引的情况下
- 3.没有索引的情况下
- 四、项目中的真实应用
- 1.首先开启spring事务
- 2.执行修改操作(业务逻辑) (根据主键或者有索引的字段进行for update查询 此操作为行锁)
- 3.最后返回执行结果给前端做展示
- 五、想要使用for update一定要开启事务否则不生效
- 总结
一、for update 是什么?
“SELECT … FOR UPDATE” 是一种在 SQL 中常用的锁定查询语句。它可以在查询数据时同时对查询结果中的记录加上排他锁 ,防止其他事务修改或删除这些记录。使用方法为在 SELECT 语句中加上 FOR UPDATE 子句。
例如:
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
这会查询 accounts 表中 id 为 1 的记录,并对这些记录加上排他锁。
B站视频地址:【数据库 for update 详细教程(行锁还是表锁问题演示)-哔哩哔哩】 https://b23.tv/4XToMlN
二、我们通常什么情况下会用到它?
1 .在我们的实际业务场景中,有些情况并发量不太高,为了保证数据的正确性,使用悲观锁也可以
1.分布式下并发扣减库存的操作(原来我们系统运用乐观锁限制 但是后来发现多扣减的问题 后来改用了 for updte 行锁的方式) 这个我会用一个案例来讲解说明2.用户扣减积分,系统自动赠送积分的并发情况,有必要加悲观锁限制一下,防止出现积分加错的情况发生.
三、select……for update会锁表还是锁行?
我将从三方面演示这个问题(以下内容都为本人亲测 mysql 5.7 数据库引擎为 innodb 隔离级别为数据库默认隔离级别 (可重复读) )
首先需要关闭数据库事务的自动提交变更为手动提交事务 set autocommit=0
sql执行脚本:
上面需要sql执行脚本的可以联系我 这里我就直接粘贴图片了
以下三种情况都需要打开两个测试窗口
1.有主键的情况下
窗口1:执行如下命令:
start transaction ;select service_name from chenwx_code where id=1 for update;
将id为1的行数据锁住 等待执行commit 语句
窗口2:执行如下命令:
update chenwx_code set is_deleted = is_deleted - 1 where id = '2';update chenwx_code set is_deleted = is_deleted - 1 where id = '1';
上面的命令
id 为 2可以正常执行成功
id为1 的数据会阻塞等待。
测试id 为 2修改操作正常执行成功
窗口2 因为开启了事务手动提交所以需要正常手动commit 数据才能执行修改数据操作
执行更新id为1的数据显示锁等待超时 超过一定时间会显示锁等待超时错误
1 .以上说明有主键的情况下执行for update语句利用主键开启事务查询会锁住当前行数据 ,不会锁住其他行数据
现在执行窗口2行锁事务执行没有超时的情况下正常扣减的情况
上面 主键1和主键2的is_deleted值都为 5
执行窗口1:
执行 1 .start transation 2.和for update 查询语句 可以看到返回主键id为1的 is_deleted 的值为5
执行窗口2:
执行 1 .start transation 2.和 for update id 为1的修改语句 可以看到数据显示正在执行一直等待中…
接着在执行窗口1 提交事务 commit 显示提交成功
窗口2显示返回更新成功 说明窗口2的事务一直在阻塞等待窗口1的事务提交
最终数据返回执行结果为 主键id为1的 is_deleted值减1成功返回4
2.有普通索引或者唯一索引的情况下
亲测普通索引和唯一索引都可以的 执行情况和主键id是一致的下面是示例
现在拿普通索引来看一下执行情况
窗口1:执行start transaction 和 根据索引查找for update语句 执行结果返回is_deleted为4的执行结果
窗口2:执行start transaction 和 根据索引字段为service_name= base2的值更新字段is_deleted的值减1正常执行并且返回成功
接着在修改索引字段为service_name= base1的值更新字段is_deleted的值减1则显示这行数据处于阻塞状态
此时执行窗口1执行commit操作 提交返回成功
然后快速的打开窗口2显示修改成功
窗口2提交commit操作则显示成功
此时查看表里面service_name=base1的数据中的is_deleted字段变为3 说明修改大成功
3.没有索引的情况下
窗口1:执行start transaction 和 根据没有索引的字段执行 for update查询语句 执行结果返回service_name=base1的这行所有数据
窗口2:执行start transaction和修改service_name=base2的这行数据则显示base2的数据处于阻塞状态
因为修改的数据不是同一行数据也就是说不是锁住的行数据 所以说是表锁住了而不是行锁
接在窗口1执行commit操作提价数据
紧接着打开窗口2 此时窗口2修改的这一行数据已经显示执行成功了所以说是表锁住了
四、项目中的真实应用
1.首先开启spring事务
2.执行修改操作(业务逻辑) (根据主键或者有索引的字段进行for update查询 此操作为行锁)
3.最后返回执行结果给前端做展示
代码情况如下所示
//开启事务 spring默认的隔离级别 @Transactional(rollbackFor = Exception.class)//根据业务执行逻辑如下//1 先使用行锁 锁住当前行数据GoodsSku goodsSku = goodsSkuService.getBaseMapper().selectList(new LambdaQueryWrapper<GoodsSku>().eq(GoodsSku::getId, proOrderGenerateSpuVo.getSkuId()).select(GoodsSku::getStock, GoodsSku::getId, GoodsSku::getVersion).last(" for update")//2 在执行修改库存操作boolean result = goodsSkuService.updateById(goodsSku);//3 提交返回数据return vo
五、想要使用for update一定要开启事务否则不生效
总结
希望大家多多支持我是维新的博客 有什么问题可以咨询我