美文网首页
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