主要针对并发情况下,通过redis的分布式锁和队列的方式进行处理的代码
Queue:{商品ID}: 数据类型是有序集合(zset),成员是用户ID,score是用户入队的时间戳
Lock:Queue:{商品ID}: 数据类型是字符串(string),存储的是该锁的过期时间
goods:{商品ID}:stock: 存储的是商品的库存数量
简单介绍demo代码中的实现思路:
将当前秒杀的商品id作为一个队列名称
$queue_name = “Queue:{商品ID}”;
对$queue_name进行加锁
通过setnx(满足原子性)实现加锁 :$redis->setnx("Lock:Queue:{商品ID}", $expire time)
加锁成功,给该锁设置一个过期时间,主要是为了防止死锁
如果加锁失败,通过设置休眠时间,进行循环请求
加锁成功后,判断队列中的成员数是否超过指定的大小
$count = $this->redis->zCard("Queue:{$name}");
if($count >= $this->redis->get("goods:{$name}:stock")) {
$this->lockModel->unlock("Queue:$name");
return '超过指定集合数量';
}
判断用户ID是否存在队列中,如不存在则加入队列(score:存入的是当前时间戳 )
if (false === $this->redis->zScore("Queue:$name", $user_id)) {
$this->redis->zAdd("Queue:$name", $score, $user_id);
}
入队成功,进行解锁$redis->del("Lock:Queue:{商品ID}");提示用户抢购成功。成功的用户会跳转到确认购买的页面,点击确认后才会生成订单、出队等后续操作
ps: 针对多个账号,一次性发送多个请求可以通过ip的访问频率的限制来预防