简介

做秒杀的商品数量一般计较少
该方案使用set存储 一定库存
每个用户抢购时,从redis申请序号, 取到了则创建订单。

可与前端缓存方案 限流 熔断一起使用

测试

1
2
3
4
5
6
7
8
cat > test.lua <<EOF
local t1 = redis.call('scard', "aaa")
local t2 = tonumber(t1)
local t3 = redis.call('exists', "aaa")
return t1;
EOF

redis-cli --ldb --eval test.lua a , b

方式a

在抢购开始前,由定时任务,或其他方式,在redis中添加商品

用户抢到购买资格后, 由另外的线程去为该用户生产订单

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
cat > sbuy.lua <<EOF
local apples = string.format("sapples-{%s}", KEYS[1]) -- 某商品库存
local holdNumUser = string.format("sbuyed-{%s}", KEYS[1]) -- 某商品已购者set集合
local buyId = ARGV[1] -- 购买人id

if redis.call('sismember', holdNumUser, buyId) > 0 
then return -2; -- 已抢购到商品
elseif redis.call('scard', apples) < 1
then return -1; -- 商品库存不足,秒杀失败
end

-- 取出一个商品,同时将购买人
local productSerialNo = redis.call("spop", apples);
redis.call('sadd', holdNumUser, buyId);
return productSerialNo;
EOF

##-- 商品id, 购买人id
PRODUCTID=P666
BuyId=u001

##-- 添加商品--由java代码调用添加带秒杀商品
redis-cli sadd sapples-{$PRODUCTID} 1 2 3
redis-cli scard sbuyed-{$PRODUCTID}
redis-cli smembers sbuyed-{$PRODUCTID}

redis-cli exists sbuyed-{$PRODUCTID} 

redis-cli EVAL "$(cat sbuy.lua)" 1 $PRODUCTID $BuyId

redis-cli --ldb --eval sbuy.lua $PRODUCTID , $BuyId

方式b

商品购支付成功后,才认为秒杀结束
当用户获得抢购资格时, 将商品序号加入带支付set中

库存没有了,可以提示用户还有未付款订单,可以再等等 当该商品支付成功后, 从待支付中移除该序号,即可

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
cat > sbuy02.lua <<EOF
local apples = string.format("sapples-{%s}", KEYS[1]) -- 某商品库存
local holdNumUser = string.format("sbuyed-{%s}", KEYS[1]) -- 某商品已购者set集合
local waitpay = string.format("switpay-{%s}", KEYS[1]) -- 某商品待支付序号set集合
local buyId = ARGV[1] -- 购买人id

if redis.call('sismember', holdNumUser, buyId) > 0 
then return -2; -- 已抢购到商品
elseif redis.call('scard', apples) < 1
then -- 待支付序号数量 > 0 则还有未付款商品, 否则秒杀结束
  if redis.call('scard', waitpay) > 0 then return -3; end
return -1; -- 商品库存不足,秒杀失败
end

-- 取出一个商品,同时将购买人
local productSerialNo = redis.call("spop", apples);
redis.call('sadd', holdNumUser, buyId);
redis.call('sadd', waitpay, buyId);
return productSerialNo;
EOF

当某笔订单成功支付,则从redis中移除该 waitpay 的序号
当订单被取消,则重新加入 apples 中

方式c

redis以计数器的方式 为用户提供抢购服务