• 使用 redis 减少 秒杀库存 超卖思路


    --------------------------------------------------- 2016 04 21 -------------------------------------------------------

    由于数据库查询的及插入的操作 耗费的实际时间要耗费比redis 要多,

    导致 多人查询时库存有,但是实际插入数据库时却超卖

    redis 会有效的减少相关的延时,对于并发量相对较少的 可以一用

    public function buy($goods_id = 0){
    	if(!$goods_id){
    		die("商品不存在!");
    	}
    	$redis = new Redis();
    	$redis->connect('127.0.0.1',6379);
    	$stock = 0;
    	if(!$redis->get("gid".$goods_id)){
    		$stock = get_stock($goods_id); //从数据看获取实际库存
    		$redis->set("gid".$goods_id,$stock);
    	}else{
    		$stock = $redis->get("gid".$goods_id);
    	}
    
    	if($stock > 0){
    		//逻辑操作 代码
    		//coding here...
    		set("gid".$goods_id,$stock-1);
    	}else{
    		die("已卖完!");
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    --------------------------------------------------- 20180918-------------------------------------------------------

    哈哈,有些打脸的节奏,之前写的其实对并发 并没有什么深刻的理解。

    正如下面评论所说,上面的代码不涉及任何并发问题,亦不能解决任何并发问题,事实上根据redis 解决超卖的思路

    我们目前有两条路可选,一是redis自带的队列,二是redis自身的事务机制,下面贴上部分代码,仅供参考

    一、redis自带队列解决问题

    //http://www.redis.net.cn/order/
    
    //1. 先将商品库存 存入队列
    $redis = new Redis();
    for($i=1;$i<=100;$i++){
        $redis->lpush('good','good_id'.$i);
    }
    print_r($redis->lrange('good',0,-1));exit;
    
    //2. 队列程序执行
    
    header("content-type:text/html;charset=utf-8");
    $redis = new Redis();
    //插入抢购数据
    $userid = "user_id_".mt_rand(1, 9999).'_'.microtime(true);
    if($res = $redis->lpop('good')){
        //$left = $redis->llen('good'); //剩余".($left)."
        $redis->lpush('good_res',$res);
        //file_put_contents('F:.txt',$userid."抢购成功!".$res."
    ",FILE_APPEND); 写入文件可能会遇到并发锁 导致无法及时写入 而被直接跳过导致记录结果有误 建议测试使用mysql 或者 redis 存入日志记录
    }else{
        //file_put_contents('F:.txt', $userid."手气不好,再抢购!
    ",FILE_APPEND);
    }
    exit;
    //3. 打印执行结果
    $redis = new Redis();
    print_r($redis->lrange('good_res',0,-1));exit;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    二。 redis 事务解决问题

     header("content-type:text/html;charset=utf-8");
            $redis = new Redis();
    //        $redis->del('good_res');                      //初始化操作
    //        $redis->set("mywatchkey",0); exit;     //初始化操作
    //        print_r($redis->lrange('good_res',0,-1));exit; //打印结果操作
            $redis->watch("mywatchkey");
            $mywatchkey = $redis->get("mywatchkey");  // 注意GET操作必须放在 WATCH 之后 否则会出现结果超卖
            $rob_total = 100;   //抢购数量
            if($mywatchkey < $rob_total){
                $redis->multi();
                //插入抢购数据
                $userid = "user_id_".mt_rand(1, 9999).'_'.microtime(true);
                $redis->lpush("good_res",$userid);
                $redis->set('mywatchkey',$mywatchkey + 1);
                $rob_result = $redis->exec();
                if($rob_result){
                    //file_put_contents('F:.txt',$userid."抢购成功!
    ",FILE_APPEND); //文件可能会遇到并发锁 导致无法及时写入 而被直接跳过导致记录结果有误 建议测试使用mysql 或者 redis 存入日志记录
                }else{
                    //file_put_contents('F:.txt', $userid."手气不好,再抢购!
    ",FILE_APPEND);exit;
                }
            }
            exit;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    上面的两种方式,我们使用apace的 ab 并发测试,没有发现超卖。

    当然,并发并非问题并非只有redis 能解决,我们仍然可以使用其他的方式,比如 订单队列,例如12306 中的当前排队多少人

    其实就是一个队列的问题,将异步问题改成同步问题来解决,当然,如果你们有更好的解决办法,欢迎评论一起研究。

  • 相关阅读:
    第二批入围企业公示!年度TOP100智能网联供应商评选
    AWS 中文入门开发教学 11- 建立公私网 - 公私分明才能网络安全
    【element-ui】el-date-picker 之picker-options时间选择区间禁用效果的实现
    LDAP服务器如何重启
    从0到1搭建大数据平台之数据存储
    Docker容器化部署企业级应用集群
    基于Python爬虫的股票成交量数据抓取分析系统
    Linux学习笔记
    【SQL Server】外键约束
    Rust 实现日志记录功能
  • 原文地址:https://blog.csdn.net/m0_67401270/article/details/126553627