文章目录
一、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执行脚本:
以下三种情况都需要打开两个测试窗口
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语句利用主键开启事务查询会锁住当前行数据 ,不会锁住其他行数据
上面 主键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一定要开启事务否则不生效
总结
原文地址:https://blog.csdn.net/qq_39220971/article/details/130245812
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_9343.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!