秒杀系统之超买问题详解

1.场景

假设现在库存只有一个商品了,多线程下如何保证最后库存是0而不是负数

2. 方法

  • MySQL中的排他锁
update goods set num = num - 1 WHERE id = 1001 and num > 0

排他锁又称为写锁,简称X锁,顾名思义,排他锁就是不能与其他所并存,如一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务是可以对数据就行读取和修改。
就是类似于我在执行update操作的时候,这一行是一个事务(默认加了排他锁)。这一行不能被任何其他线程修改和读写

  • 采用了版本号的方式
select version from goods WHERE id= 1001;
update goods set num = num - 1, version = version + 1 WHERE id= 1001 AND version = @version(上面查到的version);
  1. 这种方式采用了版本号的方式,其实也就是CAS的原理
  2. 假设此时version = 100, num = 1;100个线程进入到了这里,同时他们select出来版本号都是version = 100。 然后直接update的时候,只有其中一个先update了,同时更新了版本号。那么其他99个在更新的时候,会发觉version并不等于上次select的version,就说明version被其他线程修改过了。那么我就放弃这次update
  • 利用redis

利用redis的单线程预减库存。比如商品有100件。那么我在redis存储一个k,v。例如 <gs1001, 100>
每一个用户线程进来,key值就减1,等减到0的时候,全部拒绝剩下的请求。
那么也就是只有100个线程会进入到后续操作。所以一定不会出现超卖的现象

3.总结

  1. 第二种方式而且还应该在执行该sql语句前增加一个num数目是否大于0的业务逻辑判断,而且该方式还是会加排它锁的。
  2. 实际上,第一种方法和第二种两种方式解决超卖的方式也有细微的一点区别
  1. 考虑两个线程,当库存数量为2时。
  2. 如果是第一种方式,那么两个线程都能成功执行。
  3. 如果为第二种方式,如果在第一个线程提交事务之前,第二个线程也执行了相同的sql拿到了version值(也就是线程1和线程2拿到了相同的version值),那么这两个线程之间将只有一个线程能够让库存数目减一成功执行。最终库存数目不为0,而为1
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值