这段时间看了些秒杀业务处理方案,学到了一些东西,可能是比较粗浅的,但是万丈高楼平地起,所以还是做一个记录。
本文内容包括如下几点:
学而时习之,不亦乐乎!
以下的方案重点在于防止超卖,库存信息不加载到缓存Redis,而是直接同DB交互,实际场景下通常不会如此,但是其中用到的细节还是值得学习的。
该方案是在MySQL层面进行加锁,行锁Or表锁,要根据Where条件来判定。
该方案通过事务+for update
进行保证,伪代码如下所示:
begin transcational
count = SELECT number FROM seckill WHERE seckill_id=? FOR UPDATE
if count > 0
UPDATE seckill SET number=number-1 WHERE seckill_id=?
INSERT order //下订单
commit
说明:
for update
那么非本次事务中的 其他SQL指令会被阻塞。where
条件为主键
或者索引列
的时候才会锁住行,即行级锁
。否则会锁表,即锁表
。当前的where条件很明确是seckill_id
为主键,所以是行级锁。该方案主要通过在执行update减少库存的时候,加上对库存大于0的判定。
begin transcational
count = UPDATE seckill SET number=number-1 WHERE seckill_id=? AND number>0
if count > 0
INSERT order //下订单
commit
其中最为核心的就是最后一个条件number>0
核心就是将库存设置为无符号整形
,就是不允许库存为负数
begin transcational
count = UPDATE seckill SET number=number-1 WHERE seckill_id=?
if count > 0
INSERT order //下订单
commit
这点同where条件有些像。
通过乐观锁来保证商品在每一个只会被消费一次,通过对number
进行乐观锁来进行判定,伪代码如下所示:
BEGIN transcational
n = SELECT goods number
count = UPDATE seckill SET number=number-1 WHERE seckill_id=? AND number = n
if count > 0
INSERT order //下订单
else
Loop //再次循环操作
commit
说明
通过分布式锁来保证,同一时间只会有一个线程在处理某类商品秒杀业务:查询库存-->判定库存 -->减少库存。
dis-lock goods type //1. 通过分布式锁:锁住商品类型
begin transcational //2. 开启事务
count = SELECT number FROM seckill WHERE seckill_id=?
if count > 0
UPDATE seckill SET number=number-1 WHERE seckill_id=?
INSERT order //下订单
// dis-un-lock goods type 错误释放分布式锁
commit //3. 提交事务
dis-un-lock goods type //4. 释放分布式锁
说明
请注意3和4 的顺序,一定要先提交事务后,再释放分布式锁。
为什么了?假设此时的库存为1。第一个线程在第提交事务前释放了锁,假设提交事务需要5个单位的时间。另外一个线程在第一个线程释放锁的瞬间,抢占了锁,然后在3个时间就完成了查询和减少库存并提交事务的操作,此时库存为0。2个单位后第一个线程事务提交才完成,此时库存为-1了。这样导致了超卖。
所以事务的提交一定要释放分布式锁之前。
在Spring中事务通过是通过注解@Transcational
来实现的,如果直接在@Transcational
包裹的方法里面获取锁和释放锁可能会出现超卖,此时需要通过另外一个AOP进行包装,这里涉及到2个知识点。
@order
来定义顺序,越小的越先执行,而@Transcational
的order是最大,所以肯定是在内部执行。因为Reis
是单线程的,所以可以通过其特性decr
后进行判定,实际场景也是推荐这么做的。
Redis
中。Redis
中缓存的商品数量做decr
减1操作,如果小于0则将商品id加入商品售卖完成缓存
中。避免每次都往Redis请求。技术是为了解决业务难题而存在和发展的,切记脱离业务来学习技术。
上述方案都是解决秒杀业务的初步原形,大概思路如上所述,其中其实有很多细节,后续抽时间补上。
业务处理上需要结合自己的业务进行扩展,个人推荐:
redis > 乐观锁 > 库存大于0 > 分布式锁 > for update