利用redis List队列简单实现秒杀 PHP代码实现

利用redis List队列简单实现秒杀 PHP代码实现

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m_nanle_xiaobudiu/article/details/80479666

一 生产者producer部分

 

--------------------------------producer 部分注释------------------------------------------------------------

用户在页面请求之后, 获取到用户uid , 跳转到这个加入队列的方法 (这里直接在producer中模拟了多个uid)

在方法内部判断redis队列长度是否已经达到要求, 如果没有超出, 则执行加入队列的操作 (这里为了简洁,没有封装成方法)

注: producer.php没有进行数据库的操作,只有接受uid和其他值的操作, 数据库操作一律放在消费者consumer.php中

--------------------------------producer 注释结束-----------------------------------------------------------------------

 

生产者代码 producer.php:

  1.  
    <?php
  2.  
     
  3.  
    //连接redis数据库
  4.  
    $redis = new Redis();
  5.  
    $redis->connect('127.0.0.1',6379);
  6.  
    $redis_name = 'secKill3';
  7.  
     
  8.  
    //模拟100人请求秒杀(高压力)
  9.  
    for ($i = 0; $i < 100; $i++) {
  10.  
    $uid = rand(10000000, 99999999);
  11.  
    //获取当前队列已经拥有的数量,如果人数少于十,则加入这个队列
  12.  
    $num = 10;
  13.  
    if ($redis->lLen($redis_name) < $num) {
  14.  
    $redis->rPush($redis_name, $uid);
  15.  
    echo $uid . "秒杀成功"."<br>";
  16.  
    } else {
  17.  
    //如果当前队列人数已经达到10人,则返回秒杀已完成
  18.  
    echo "秒杀已结束<br>";
  19.  
    }
  20.  
    }
  21.  
    //关闭redis连接
  22.  
    $redis->close();
  23.  
     

 

 

 

注: 执行完producer.php文件,本地redis数据库第0号数据库中应该有一个键名为"secKill3"的List队列,像这样

 

 

二 消费者consumer部分

 

------------------------------消费者部分注释---------------------------------------------

消费者一直读取redis数据库中指定队列,一有值,立即取出,并进行相应数据库操作

------------------------------消费者部分注释结束----------------------------------------

 

消费者代码 consumer.php

 

  1.  
    <?php
  2.  
     
  3.  
    //设置redis数据库连接及键名
  4.  
    $redis = new Redis();
  5.  
    $redis->connect('127.0.0.1');
  6.  
    $key = 'secKill3';//redis数据库key [注:默认redis数据库选择第0号数据库]
  7.  
     
  8.  
    //PDO连接mysql数据库
  9.  
    $dsn = "mysql:dbname=test;host=127.0.0.1";
  10.  
    $pdo = new PDO($dsn, 'root', '123456');
  11.  
     
  12.  
    //死循环
  13.  
    //从队列最前头取出一个值,判断这个值是否存在,取出时间和uid,保存到数据库
  14.  
    //数据库插入失败时,要有回滚机制
  15.  
    //注: rpush 和lpop是一对
  16.  
     
  17.  
    while(1) {
  18.  
    //从队列最前头取出一个值
  19.  
    $uid = $redis->lPop($key);
  20.  
    //判断值是否存在
  21.  
    if(!$uid || $uid == 'nil'){
  22.  
    sleep(2);
  23.  
    continue;
  24.  
    }
  25.  
    //生成订单号
  26.  
    $orderNum = build_order_no($uid);
  27.  
    //生成订单时间
  28.  
    $timeStamp = time();
  29.  
    //构造插入数组
  30.  
    $user_data = array('uid'=>$uid,'time_stamp'=>$timeStamp,'order_num'=>$orderNum);
  31.  
    //将数据保存到数据库
  32.  
    $sql = "insert into student (uid,time_stamp,order_num) values (:uid,:time_stamp,:order_num)";
  33.  
    $stmt = $pdo->prepare($sql);
  34.  
    $res = $stmt->execute($user_data);
  35.  
    //数据库插入数据失败,回滚
  36.  
    if(!$res){
  37.  
    $redis->rPush($key,$uid);
  38.  
    }
  39.  
    }
  40.  
     
  41.  
    //生成唯一订单号
  42.  
    function build_order_no($uid){
  43.  
    return substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8).$uid;
  44.  
    }

 

 

 

注: 执行完consumer.php之后,数据库对应数据表应该有数值

 

到此,秒杀结束

 

备注:

用到的student数据表结构sql

 

  1.  
    CREATE TABLE `student` (
  2.  
    `uid` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'uid',
  3.  
    `username` varchar(20) NOT NULL DEFAULT '',
  4.  
    `time_stamp` int(11) NOT NULL DEFAULT 0,
  5.  
    `order_num` bigint(20) UNSIGNED NOT NULL DEFAULT 0,
  6.  
    PRIMARY KEY (`uid`) USING BTREE,
  7.  
    key (time_stamp)
  8.  
    ) ENGINE = MyISAM default charset=utf8;

 

 

 

 

 

 

posted @ 2019-09-21 17:33  归一山人  阅读(367)  评论(0编辑  收藏  举报