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

思路:

需要一个排队队列和抢购结果队列及库存队列。高并发情况,先将用户进入排队队列,用一个线程循环处理从排队队列取出一个用户,判断用户是否已在抢购结果队列,如果在,则已抢购,否则未抢购,库存减1,写数据库,将用户入结果队列。

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

描述:在方法内部判断redis队列长度是否已经达到要求, 如果没有超出, 则执行加入队列的操作

注意:这一步骤没有进行数据库的操作,只有接受uid和其他值的操作

先将商品库存如队列

//模拟20抢购
$uid = rand(10000000, 99999999);//这里模拟uid,其实就是用户过来的uid值,真实项目就是获取用户的id过你来就好
//连接redis数据库
$redis = new Redis();
$redis->connect('127.0.0.1',6379);
$redis_name = 'goods_store';
 
//模拟2000人请求秒杀(高压力)
$num = 20;
if ($redis->lLen($redis_name) < $num) {
   $redis->rPush($redis_name, $uid);
   echo $uid . "秒杀成功"."<br>";
} else {
    //如果当前队列人数已经达到20人,则返回秒杀已完成
      echo "秒杀已结束<br>";
 }
//关闭redis连接
$redis->close();

执行完后,本地redis数据库第0号数据库中应该有一个键名为"goods_store"的List队列,像这样

2、对数据库的操作

抢购、描述逻辑

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

<?php
 
//设置redis数据库连接及键名
$redis = new Redis();
$redis->connect('127.0.0.1');
$key = 'goods_store';//redis数据库key [注:默认redis数据库选择第0号数据库]
$sku_id=11;//商品id
$number=1;//每次都减1
 
//PDO连接mysql数据库  若是用到框架的话,数据库的链接这步骤省去
$dsn = "mysql:dbname=test;host=127.0.0.1";
$pdo = new PDO($dsn, 'root', '123456');
 
 //死循环
//从队列最前头取出一个值,判断这个值是否存在,取出时间和uid,保存到数据库
//数据库插入失败时,要有回滚机制
//注: rpush 和lpop是一对
 
while(1) {
    //从队列最前头取出一个值
    $uid = $redis->lPop($key);
    //判断值是否存在
    if(!$uid || $uid == 'nil'){
        sleep(2);
        continue;
    }
    //生成订单号
    $orderNum = build_order_no($uid);
    //生成订单时间
    $timeStamp = time();
    //构造插入数组
    $user_data = array('uid'=>$uid,'time_stamp'=>$timeStamp,'order_num'=>$orderNum);
    //将数据保存到数据库
    $sql = "insert into student (uid,time_stamp,order_num) values (:uid,:time_stamp,:order_num)";
    $stmt = $pdo->prepare($sql);
    $res = $stmt->execute($user_data);
    //数据库插入数据失败,回滚
    if(!$res){
        $redis->rPush($key,$uid);
    }else{//减少货存
        $sql="update store set number=number-{$number} where sku_id='$sku_id'";
        $store_rs=mysql_query($sql,$conn); 
        if(mysql_affected_rows()){ 
            insertLog('库存减少成功');
        }else{ 
            insertLog('库存减少失败');
        }
    }
    
}
 
//生成唯一订单号
function build_order_no($uid){
    return  substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8).$uid;
}

总结:使用redis队列,因为pop操作是原子的,即使有很多用户同时到达,也是依次执行,推荐使用(mysql事务在高并发下性能下降很厉害,文件锁的方式也是)

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值