在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
有一个场景,商品A预售量1000件,早上10点准时开抢,10W个人一起来抢,在正式开始之后,我们将面对两个问题
storage:16 指的是ID为16的商品库存,也建议在添加抢购活动的时候就缓存到redis中
好,准备工作做好了,正式开始! $redis = new RedisService(); //购买数量不得大于限购数量 $productInfo = $redis->hashGet("productInfo:".$productId); if($productInfo['limitBuy'] < $num) { echo json_encode(["code"=>30017,"msg"=>'每人限购'.$productInfo['limitBuy']."件"]);die(); } //加分布式锁,原子化下单流程 $storageLockKey = "storage:".$productId; $expireTime = $redis ->lock($storageLockKey,5,200); //判断商品库存 $storageKey = $this->getStorageKey($productId); $storage = $redis->get($storageKey); if($storage <= 0 || $storage < $num) { $redis->unlock($storageLockKey,$expireTime); echo json_encode(["code"=>30018,"msg"=>'库存不足']);die(); } //欲购买数量+已购买数量 不得超过限购数量 $limitBuyKey = "product:limitBuy:".$productId; if($redis->getZsetScore($limitBuyKey,"user_".$userId)+$num > $productInfo['limitBuy']) { $redis->unlock($storageLockKey,$expireTime); echo json_encode(["code"=>30019,"msg"=>'每人限购'.$productInfo['limitBuy']."件"]);die(); } $orderInfo = array( 'buyer_id' => $userId,#用户ID 'product_id' => $productId,#产品ID 'num' => $num, #购买数量 'price'=>$productInfo['price'], 'pay_type'=>1 //在线支付 ); //订单放进队列 $orderKey = "orderList"; $orderRe = $redis->push($orderKey,serialize($orderInfo)); //下单成功 if($orderRe) { //获取原有集合元素个数 $count = $redis->countZset($limitBuyKey); //记录购买人和购买数量 $redis->alterZsetScore($limitBuyKey,"user_".$userId,$num); //如果是第一次插入元素,设置过期时间+3天,防止内存堆积 if(!$count) { $recordExpire = strtotime($productInfo['endTime']) - strtotime($productInfo['beginTime']) +3*24*3600; $redis->expire($limitBuyKey,$recordExpire); } //商品减少库存 $redis->alterNumber($storageKey,-$num); $redis->unlock($storageLockKey,$expireTime); echo json_encode(["code"=>200,"msg"=>'下单成功']);die(); } 可以看到,我们的下单流程是 加分布式锁 -> 判断库存 -> 限购 -> 订单信息放入Redis列表 我们重点来看这个分布式锁如何实现 这里的锁保存的值是一个过期时间的时间戳(毫秒级) 有人说为什么要记录个时间戳呢?为什么不直接利用redis的自动过期时间呢? 流程解释: 当前没有锁,则获得锁,返回过期时间 如果过期则更新过期时间,getset获得锁,判断返回的的过期时间是否已经被抢先重置了,被抢先则等待20毫秒,没被抢先则返回过期时间 这样就可以保证 永远只有一个进程获得锁,永远只有一个进程在进行库存的相关判断和操作,防止超卖 并发5000次请求 10秒完成,平均每秒500个并发 可以每人买一件的话,生成了2000个订单,写入redis列表 库存为0 没有出现超卖的情况 接下来后来启动个定时任务,每分钟启动一次,每次弹出3000个元素,写入数据库
linux添加定时任务 过一分钟我们看到,队列里的订单已被弹出批量写入数据库了 在这个过程中,我们这两个问题都得到了较好地解决
转载 来自:https://blog.csdn.net/weixin_40325128/article/details/89378834 |
2022-08-18
2022-08-16
2022-11-06
2022-08-18
2022-08-15
请发表评论