redis 实现秒杀 简单示例

我们正常写一个购买商品:然后用ab测试下 ab -n 300 -c 100 -k http://laravel8.cn/api/Index/Ceshi
发现:库存变成了负数了。

public function ceshi(){
        $info = DB::table('shop')->where("id","2")->get()->map(function ($value) {
            return (array)$value;
        })->toArray();
        if($info[0]['shopNum'] > 0) {
            sleep(1);
            $result = DB::table('shop')->where("id", "2")->decrement('shopNum');
            if($result){
                echo '执行成功';
            }
            return;
        }
        echo '没有库存了';exit;
    }

在这里插入图片描述
秒杀抢购时同时大量的用户同时下单并发量大才导致超卖。
解决思路:我们把商品放在redis里用redis 处理并发(官方测试redis每秒处理大概8万-10万)。把商品放在redis列表里,然后 判断 列表长度 是否大于0,如果大于0 就继续下单 。每下一单把队列 lpop出去一个 把商品信息、用户信息、订单号放在队列里。通过异步进程进行处理队列 进入mysql 表。
通过定时任务 把秒杀商品放入redis

public function insertShopRedis(){
        $shopData = DB::table("shop")->get()->map(function ($value) {
            return (array)$value;
        })->toArray();
        if(!empty($shopData)){
            foreach($shopData as $k=>$v){
                // 需要存入redis
                for($i=1;$i<=$v['shopNum'];$i++){
                    Redis::lpush('shop'.$v['id'],$i);
                }
                Redis::setex('shopNum'.$v['id'],120,$v['shopNum']);
                Redis::expire('shop'.$v['id'], 120);
            }
            echo '秒杀商品放入完毕';exit;
        }else{
            echo '没有要秒杀的商品';exit;
        }
    }

点击下单:

public function redisSeckill(Request $request){
        $id = 2;//这里的商品id $request 拿过来的
        $userId = rand(1,99); // userId 用户的id我随机生成的
        $key = 'shop'.$id;
        $result = Redis::exists($key);
        if($result == false){
            echo '秒杀时间已过';exit;
        }
        if(Redis::llen($key) <= 0){
            echo '抱歉已无库存';exit;
        }
        if (Redis::llen('user_list') > Redis::get("shopNum".$id)) {
            echo '抱歉队列大于商品的数量';exit;
        }
        // 用户信息存入队列里
        $result = $this->requestUser($userId,$id);
        if(!$result){
            echo '抱歉你已经抢购过该商品了';exit;
        }
        $count = Redis::lpop($key);
        if(!$count) {
            echo '抱歉已无库存';exit;
        }
        // 测试直接更改表
        $result = DB::table('shop')->where("id", "2")->decrement('shopNum');
        if($result){
            echo "购买成功";
        }
        return;
        // 实际:商品、用户购买的信息进入队列
        $arr['userId'] = $userId;
        $arr['shopId'] = $id;
        $arr['order_on'] = $this->orderNo();
        $str = json_encode($arr);
        Redis::lpush('order_list'.$id,$str);
        echo $userId.'购买成功';exit;
    }
    // 需要对已经在队列的用户进行截止
    private function requestUser($userId,$id)
    {
        $userArr = Redis::Lrange('user_list'.$id,0,-1);
        if(!in_array($userId,$userArr)) {
            Redis::lpush('user_list'.$id, $userId);
            return true;
        }
        return false;
    }
    // 生成订单号
    private function orderNo(){
        return date('ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值