使用 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("已卖完!");
	}
}

--------------------------------------------------- 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;

二。 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;

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值