突然想写点关于redis业务实现的一些东西,想起来很早之前看过一个关于用watch完成秒杀功能的案例,然后又翻出来看了看,不看还好, 一看发现这个实现逻辑是有问题了,随便改吧改吧,希望不要被误导
$redis = new Redis();
$redis->pconnect('127.0.0.1', 6379, 2.5);
echo "Connection to server sucessfully\n";
//check whether server is running or not
// echo "Server is running: " . $redis->ping();
$redis->select(7);
$rob_total = 100; //抢购数量
$mywatchkey = $redis->get("mywatchkey");
if($mywatchkey === false){
$redis->set("mywatchkey", $rob_total);
$mywatchkey = $rob_total;
}
if($mywatchkey <= 0){
file_put_contents('message.txt', "you are late, red packet was gone\n", FILE_APPEND );
exit;
}
$redis->watch("mywatchkey");
$mywatchkey = $redis->get("mywatchkey");
if($mywatchkey > 0 ){
$ret = $redis->multi();
//插入抢购数据 [去重]
$ret = $ret->hSetNx("mywatchlist","user_id_".uniqid().mt_rand(1, 1000),time());
$ret = $ret->decr("mywatchkey");
$rob_result = $ret->exec();
if($rob_result){
$mywatchlist = $redis->hGetAll("mywatchlist");
echo "抢购成功!<br/>";
echo "剩余数量:". ($mywatchkey-1)."<br/>";
echo "用户列表:<pre>";
var_dump($mywatchlist);
file_put_contents('message.txt', "bug sucess! remaining num:". $redis->get("mywatchkey") . "\n", FILE_APPEND );
}else{
$redis->discard();
file_put_contents('message.txt', "bug failure! unlucky go on \n", FILE_APPEND );
}
}else{
file_put_contents('message.txt', "red packet was gone\n", FILE_APPEND );
}
// $redis->close();
exit;
首先连接 这里用的pconnet 并设置2.5s的超时限制 抢购肯定是在短时间内大量的请求 所以还是持久化好点
然后这个mywatchkey取值的点也要注意下
还有这里采用的hash存放用户id,
以前我有 json(['userid'=>00001, 'time'=>time()]) 后放list的情况, 还是跟业务吧,思路多样化
对于这种实现思路 一定注意:
大量的用户涌入, 不是说你是前【$rob_total = 100】个就一定能抢到,后面的用户就没有红包可抢了,这种要基于并发考虑, 比如说前100个用户同时涌入,其中一个用户获得这个 watch key 其他用户就只能 "bug failure! unlucky go on" 继续自主加入抢的队列或者放弃了
而且watch mulit exec 会拖慢性能, 需要根据业务量 考虑相应的策略
如果对于前100用户的抢购业务策略,可以考虑list+set方式,不过也有一定的局限性
首先有个红包队列, 这里简单模拟下:
//note 预存红包
for ($i=0; $i < 1000; $i++) {
$redis->lpush('000|redpacket', time(). sprintf("%04d", mt_rand(0, 1000)));
}
然后给就是等待放出时间开始抢的业务了:
//note begin buy
$userid = mt_rand(0, 10000);
$redOne = $redis->rpop('000|redpacket');
if($redOne){
if($redis->sadd('000|redusers', $userid)){
$redis->lpush('000|consumeredlist', json_encode(['redOne'=>$redOne, 'userid'=>$userid, 'time'=>microtime(true)]));
file_put_contents('message.txt', "SUCESS: You bug success\n", FILE_APPEND );
}else{
//note: if user have already bought, Put the red packet back list
if(!$redis->lpush('000|redpacket', $redOne)) $redis->lpush('000|redpacket', $redOne);
file_put_contents('message.txt', "NOTICE: You have already bought\n", FILE_APPEND );
}
}else{
file_put_contents('message.txt', "NOTICE: you are late, red packet was gone\n", FILE_APPEND );
}
exit;
其实主要就是要根据业务场景,考虑具体实现思路,这里只是对其中一些点做些浅显的介绍