美文网首页
Redis防止超卖的两种实现

Redis防止超卖的两种实现

作者: 莴牛 | 来源:发表于2020-08-12 18:57 被阅读0次

    环境准备:mac,php7,redis,laravel

    新建个数据库表用来测试

    CREATE TABLE `goods_sold` (
      `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
      `goods_id` bigint(20) DEFAULT NULL,
      `num` bigint(20) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
    
    #插入一条数据
    INSERT INTO `goods_sold` (`goods_id`, `num`) VALUES (1, 100);
    
    • 使用redis watch 和 事务

      public function OverSoldFunctionWithRedisTransaction()
      {
            // todo 从数据库获取库存 这里忽略,直接写死一个 提前取出放入缓存中
            $total = 100;
    
            $redis = new \Redis();
            $redis->connect('127.0.0.1', 6379);
    
            $key = 'watchKey';
            $watchKey = $redis->get($key);
            if ($watchKey < $total) {
                $redis->watch($key);
    
                // 开启redis事务
                $redis->multi();
    //            sleep(1);
    
                $redis->set($key, $watchKey + 1);
                $transResult = $redis->exec();
    
                if ($transResult) {
                    GoodsSold::find(1)->decrement('num');
                    exit('抢购成功');
                } else {
                    exit('抢购失败,下次再来');
                }
            } else {
                exit('商品已抢光');
            }
        }
    

    ab压测看实际效果

    ab -n 1000 -c 100 -k http://laravel.tyl.com/over/sold
    
    压测数据
    • 重复测试需要删除redis值、同时更新数据库的库存总数和代码中一致(100)
    redis-cli -h 127.0.0.1 -p 6379
    127.0.0.1:6379> get watchKey
    "100"
    127.0.0.1:6379> del watchKey
    (integer) 1
    127.0.0.1:6379> get watchKey
    (nil)
    127.0.0.1:6379>
    
    • 使用List存储(队列)

        #先使用第一步取出库存放入队列中
        /**
         * 取出库存放入队列中
         */
        public function PushQueue()
        {
            $data = GoodsSold::find(1);
            $total = $data->num;
            if ($total > 0) {
                $redis = new \Redis();
                $redis->connect('127.0.0.1', 6379);
    
                $queueKey = 'queueKey';
                for ($i = 1; $i <= $total; $i++) {
                    $redis->lPush($queueKey, $i);
                }
            }
        }
    
        /**
         * 超卖实现二
         */
        public function OverSoldFunctionWithRedisQueue()
        {
            $redis = new \Redis();
            $redis->connect('127.0.0.1', 6379);
    
            $queueKey = 'queueKey';
    
            if ($redis->rPop($queueKey)) {
                GoodsSold::find(1)->decrement('num');
                exit('抢购成功');
            } else {
                exit('商品已抢光');
            }
        }
    

    ab压测看实际效果

    ab -n 1000 -c 100 -k http://laravel.tyl.com/over/sold/2
    

    总结

    • 方案一和二都需要将库存数据提前同步到redis当中
    • 方案二使用链表效果更好

    相关文章

      网友评论

          本文标题:Redis防止超卖的两种实现

          本文链接:https://www.haomeiwen.com/subject/dbdmdktx.html