Redis实现秒杀、抢购业务

8 篇文章 0 订阅

前言

我们实际开发中经常遇到定点秒杀业务,比如抢购商品、抢红包等等;这种情况下,一瞬间的并发访问量非常大,若设计不完善可能会出现超卖的现象。通过Redis的列表可以很好起到消峰的作用,同时实现业务之间的解耦。

环境

redis: 5.0.5
wrk: 4.1.0-4 # 压测工具(https://github.com/wg/wrk)
eg: wrk -t12 -c400 -d30s http://127.0.0.1:8080/index.html
    -c, --connections: 连接:保持打开的HTTP连接的总数,每个线程处理N = 连接/线程
    -d, --duration: 持续时间:试验持续时间,如2s, 2m, 2h
    -t, --threads: 要使用的线程总数
    -s, --script: LuaJIT脚本
    -H, --header: 添加到请求的HTTP头文件,例如。“用户代理:wrk
        --latency: 打印详细的延迟统计信息
        --timeout: 如果在这段时间内没有收到响应,则记录超时。

需求

  1. 活动:抢购10件商品
  2. 使用Redis列表(LIST)实现

方案

  1. 判断列表长度是否小于10,如果小于10就向列表加入用户信息,表示用户抢到免单资格,否则就没有抢到;
    代码:seckill_1.php
    <?php
    // 加载redis组件
    $redis = new Redis();
    $redis->connect('127.0.0.1', 6379);
    // 秒杀任务Redis队列名称
    $redis_name = 'seckill';
    
    // 模拟用户ID
    $uid = mt_rand(1, 99999999);
    
    // 如果数量小于10,加入队列
    if ($redis->lLen($redis_name) < 10) {
        // 将用户ID和时间采用%拼接加入队列
        $redis->rPush($redis_name, $uid . '%' . microtime());  
        echo $uid . "秒杀成功\n";
    } else {
        echo "秒杀已结束\n";
    }
    $redis->close();
    
    ---------------------------------------------------------------------------
    接下来我们采用wrk并发测试
    [root@Lewis ~]# /usr/local/wrk/wrk -t12 -c300 -d5s http://127.0.0.1:1010/queue_redis/seckill_1.php
    Running 5s test @ http://127.0.0.1:1010/queue_redis/seckill_1.php
      12 threads and 300 connections
      Thread Stats   Avg      Stdev     Max   +/- Stdev
        Latency   500.58ms  472.27ms   1.99s    71.63%
        Req/Sec    27.74     28.30   242.00     89.31%
      1133 requests in 5.04s, 235.73KB read
      Socket errors: connect 0, read 0, write 0, timeout 294
    Requests/sec:    224.60
    Transfer/sec:     46.73KB
    
    正常情况下seckill列表中应该只有十条记录,我们验证一下
    [root@Lewis ~]# redis-cli 
    127.0.0.1:6379> LRANGE seckill 0 -1
     1) "57622090%0.74547100 1578362569"
     2) "96859777%0.74460200 1578362569"
     3) "9940359%0.74467400 1578362569"
     4) "62113889%0.74470900 1578362569"
     5) "10321533%0.74474300 1578362569"
     6) "4424149%0.74484300 1578362569"
     7) "85393877%0.74463800 1578362569"
     8) "11917405%0.74480800 1578362569"
     9) "96283400%0.74453800 1578362569"
    10) "93670956%0.74490200 1578362569"
    11) "55303655%0.74495300 1578362569"
    12) "75197864%0.74501900 1578362569"
    我们发现列表有12条记录,这就是并发所带来的超卖问题。
    
    

     

  2.  利用Redis的pop操作的原子性来实现秒杀。先定义一个长度为10的列表,请求时如果能从列表中pop出数据,表示抢购到,否则抢购失败。

    代码:seckill_2.php
    <?php
    // 加载redis组件
    $redis = new Redis();
    $redis->connect('127.0.0.1', 6379);
    $redis_name = 'seckill';
    $new_name = 'lucky_dog';
    
    // 从队列最左侧取出一个值
    $flag = $redis->lPop($redis_name);
    
    // 值不存在,直接范围失败
    if (!$flag) {
        return json_encode(['status' => 0, 'msg' => "秒杀已结束"]);
    }
    // 模拟用户ID
    $uid = mt_rand(1, 99999999);
    // 将有资格用户加入新列表中,以便其他逻辑操作(比如再起一个进程保存到数据库)
    $redis->rPush($new_name, $uid . '%' . microtime());
    $redis->close();
    
    首先,删除seckill列表,向列表中添加10条记录
    127.0.0.1:6379> DEL seckill
    (integer) 1
    127.0.0.1:6379> LPUSH seckill 1 2 3 4 5 6 7 8 9 10
    (integer) 10
    
    然后用wrk压测
    [root@Lewis ~]# /usr/local/wrk/wrk -t12 -c300 -d5s http://127.0.0.1:1010/queue_redis/seckill_2.php
    Running 5s test @ http://127.0.0.1:1010/queue_redis/seckill_2.php
      12 threads and 300 connections
      Thread Stats   Avg      Stdev     Max   +/- Stdev
        Latency   481.78ms  461.79ms   1.97s    68.52%
        Req/Sec    27.36     22.61   141.00     85.84%
      1583 requests in 5.06s, 295.27KB read
      Socket errors: connect 0, read 0, write 0, timeout 58
    Requests/sec:    312.68
    Transfer/sec:     58.32KB
    
    最后Redis验证结果
    127.0.0.1:6379> LRANGE seckill 0 -1
    (empty list or set)
    127.0.0.1:6379> LRANGE lucky_dog 0 -1
     1) "85047694%0.79134800 1578367304"
     2) "24375119%0.79158500 1578367304"
     3) "38742237%0.79166800 1578367304"
     4) "48787460%0.79199000 1578367304"
     5) "71600652%0.78556500 1578367304"
     6) "64137637%0.78562400 1578367304"
     7) "75032333%0.78537600 1578367304"
     8) "61620233%0.78551300 1578367304"
     9) "68069808%0.78525800 1578367304"
    10) "74514593%0.79245800 1578367304"
    结果符合预期结果
    
    

     

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值