Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

关于乐观锁 #24

Open
Lxiaolong opened this issue May 6, 2019 · 19 comments
Open

关于乐观锁 #24

Lxiaolong opened this issue May 6, 2019 · 19 comments

Comments

@Lxiaolong
Copy link

本人小白,mysql的update本身具有排他锁,@update("update sk_goods_seckill set stock_count = stock_count - 1, version= version + 1 where goods_id = #{goodsId} and stock_count > 0 and version = #{version}"),更新库存应该不存在安全问题,为什么还需要根据版本来实现乐观锁,希望释疑。

@zaiyunduan123
Copy link
Owner

你说的属于悲观锁解决超卖方案,每次更新前要select for update给指定商品信息加排他锁,然后阻塞其他请求,但这种情况下并发性能大大降低。

@Lxiaolong
Copy link
Author

Lxiaolong commented May 9, 2019 via email

@zaiyunduan123
Copy link
Owner

乐观锁发生冲突时版本号已经被其他请求+1,所以重试时需要获取最新的版本号,直接update还是用原来的版本号,会一直重试失败。

@Lxiaolong
Copy link
Author

Lxiaolong commented May 9, 2019 via email

@zaiyunduan123
Copy link
Owner

直接update还是会导致超卖问题,如果需要用排他锁,update之前要select for update申请排他锁才可以。

@Lxiaolong
Copy link
Author

Lxiaolong commented May 9, 2019 via email

@Lxiaolong
Copy link
Author

Lxiaolong commented May 9, 2019 via email

@wenbochang888
Copy link

stock_count > 0 这个条件已经是乐观锁了。根本不会出现超卖现象。那个版本号原理就是cas,在这里有点多此一举。同时redis是单线程,根本不会有两个线城同时减库存,所以不可能存在超卖现象

@zaiyunduan123
Copy link
Owner

确实是这样的,因为加了库存判断,这就已经解决超卖问题,是我理解有误。

@worlse
Copy link

worlse commented Jul 31, 2019

学习了,谢谢,刚刚我也是在这个环境又疑惑,5次判断乐观锁,如果数量不是 更新减一,而是用户自己下单的数量减N,那这一步是比较好的

@fang179364
Copy link

afterPropertiesSet();这个同步mysql和redis库存的操作,因为mysql和redis的速率不同导致redis上的库存已经用完但是mysql还没来得及更新,然后afterPropertiesSet()会把redis上的库存同步为mysql上一样的,会不会产生大量的redis上的超卖

@Lxiaolong
Copy link
Author

afterPropertiesSet();这个同步mysql和redis库存的操作,因为mysql和redis的速率不同导致redis上的库存已经用完但是mysql还没来得及更新,然后afterPropertiesSet()会把redis上的库存同步为mysql上一样的,会不会产生大量的redis上的超卖

不会,这个同步是初始化的时候同步,redis库存用完意味着此次秒杀结束

@fang179364
Copy link

long stock = redisService.decr(GoodsKey.getGoodsStock, "" + goodsId);//10
if (stock < 0) {
afterPropertiesSet();
long stock2 = redisService.decr(GoodsKey.getGoodsStock, "" + goodsId);//10
if(stock2 < 0){
localOverMap.put(goodsId, true);
return Result.error(CodeMsg.SECKILL_OVER);
}
}
但是代码上写的逻辑就是redis上的库存一为0就会更新mysql上的缓存数量

@Lxiaolong
Copy link
Author

long stock = redisService.decr(GoodsKey.getGoodsStock, "" + goodsId);//10
if (stock < 0) {
afterPropertiesSet();
long stock2 = redisService.decr(GoodsKey.getGoodsStock, "" + goodsId);//10
if(stock2 < 0){
localOverMap.put(goodsId, true);
return Result.error(CodeMsg.SECKILL_OVER);
}
}
但是代码上写的逻辑就是redis上的库存一为0就会更新mysql上的缓存数量

昂,你应该这样理解,超卖是出现数据库为负数的情况,所以就算redis缓存同步为上次一样的也没关系,这样的后果只是多一些人来参加秒杀,但是不会出现超卖,这些人最后的结果的是秒杀失败。

@fang179364
Copy link

我在想redis上如果不出现超卖,那mybatis上是不是也不会超卖,然后mybatis上是不是就不用加锁了

@Lxiaolong
Copy link
Author

我在想redis上如果不出现超卖,那mybatis上是不是也不会超卖,然后mybatis上是不是就不用加锁了

mysql更新操作默认加X锁,数据库主要控制好事务,下单,减库存要加事务。还有我估计这里同步还是考虑到秒杀取消订单的情况。redis单线程,你直接实现不超卖也可以。

@fang179364
Copy link

谢谢大佬代码学习了

@lzhenglin
Copy link

lzhenglin commented Jul 4, 2023

hello,你是如何测试的,然后确认的乐观锁性能要比悲观锁性能高, 并发多少 ,
我看了最早的你的想法 我觉得思路是对的 , 不太确认为什么你的测试结果却不好。 @Lxiaolong

@1gxv1
Copy link

1gxv1 commented Apr 18, 2024

这个如果只有乐观锁去判断的话,那不是秒杀的时候失败的概率会很高?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants