使用redis秒杀出现产品超发现象求解?
connect('10.10.10.119', 6379);
$mywatchkey = $redis->get("mywatchkey");
$rob_total = 100; //抢购数量
if($mywatchkeywatch("mywatchkey");
$redis->multi();
//设置延迟,方便测试效果。
sleep(5);
//插入抢购数据
$redis->hSet("mywatchlist","user_id_".mt_rand(1, 9999),time());
$redis->set("mywatchkey",$mywatchkey+1);
$rob_result = $redis->exec();
if($rob_result){
$mywatchlist = $redis->hGetAll("mywatchlist");
echo "抢购成功!
";
echo "剩余数量:".($rob_total-$mywatchkey-1)."
";
echo "用户列表:";
var_dump($mywatchlist);
}else{
echo "手气不好,再抢购!";exit;
}
}
?>
回复内容:
最近在做一个秒杀活动,处于性能和响应速度的考虑,使用了redis。写的时候就特别注意了杜绝超发现象,基于redis理论的cas(check and set)乐观锁,想着应该能够杜绝该问题,但是还是出现了,很疑惑求大神帮助,具体的代码大致如下:
connect('10.10.10.119', 6379);
$mywatchkey = $redis->get("mywatchkey");
$rob_total = 100; //抢购数量
if($mywatchkeywatch("mywatchkey");
$redis->multi();
//设置延迟,方便测试效果。
sleep(5);
//插入抢购数据
$redis->hSet("mywatchlist","user_id_".mt_rand(1, 9999),time());
$redis->set("mywatchkey",$mywatchkey+1);
$rob_result = $redis->exec();
if($rob_result){
$mywatchlist = $redis->hGetAll("mywatchlist");
echo "抢购成功!
";
echo "剩余数量:".($rob_total-$mywatchkey-1)."
";
echo "用户列表:";
var_dump($mywatchlist);
}else{
echo "手气不好,再抢购!";exit;
}
}
?>
我觉得这个代码在高并发的情况,仍然会出现超卖现象。假如:只剩 1个奖品的时候,有三个人同时执行 $redis->watch("mywatchkey")
拿到的数据是 99 ,那么就出现超卖现象了。
由于redis
是单线程读取,那么就用最简单的队列实现吧。
在抽奖前先把奖品数量,写入
redis
队列award:100
// 长度为100的 list ,值只是作为是否中奖并发抽奖
$award = $redis->lpop('award:100'); // 由于队列只有100个值,可以确保只有100个人中奖
if(!$award){
echo "手气不好,再抢购!";exit;
}
// 剩下就是中奖操作的事情了
sleep(5);
你公司还收不收人?
考虑一下这种情况。
mywatchkey=99
用户A请求mywatchkey得到99,
用户B请求mywatchkey得到99,
当A,B请求完成之后,mywatchkey应该为多少呢。。101,还是100?
你这个是经典的Check-then-Act错误
首先问一下你这个代码的“超卖”结果是$mywatchkey > 100了 (我认为不可能出现)
还是$mywatchkey =100时候同时有两个页面出现“抢购成功!“
我没有对这个代码进行测试,我只是猜测会不会是以下的情况:
1.Session A : 进行mywatchkey检查,此时mywatchkey = 99
2.Session B : 处理完 mywatchkey以后,此时mywatchkey=100 但是并不影响Session A的后续操作
补充两个连接,看看吧,或许有收获
transactions-and-watch-statement-in-redis
redis-watch-multi-exec-by-one-client
如果这事要从根本上解决,请先学习:
1.锁。
2.传统数据库事务。
然后再学习Redis与传统数据库的区别。