** PHP redis事物 监听 有序集合乐观锁 实现高并发**
看到网上有人说有锁 数据库创建的时候选择非负数… 发现都不靠谱
在高并发的时候容易出现商品超卖等现象 看见有人用lpush 队列去实现
使用redis的原子操作来实现这个“单线程” 列表的pop操作是原子的 多个人来了也要排队
但是压力测试 并发1000 以上就 出现了 问题 ,还是超卖了
$redis = new Redis();
$redis->connect('127.0.0.1',6379);
// $redis->lpush('test',1);
$num = $redis->llen('test');
echo $num;
if ($num>=1) {
// echo "ok,";
$redis->lpop('test');
$db->exec("insert into test(img) value('1')");
}else{
echo ",error";
}
并发量大的话出现了超卖
解决 使用redis乐观锁来解决 并发超卖
需要了解 redis
Watch 监听一个(或多个) key 如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断
multi 标记一个事务块的开始
zadd 有序队列
我们先将抢到的用户信息放到有序队列里面, 等秒杀完毕之后 再去完成数据库添加
`ZADD : ZADD key-name score member [score member ...] ------------将带有给定分值的成员添加到有序集合里
ZREM : ZREM key-name member [member ...] --------------------------从有序集合里移除给定的成员,并返回被移除成员的数量
ZCARD : ZCARD key-name ------------------------------------------------返回有序集合包含的成员数量
ZINCRBY : ZINCRBY key-name increment member --------------------将member成员的分值加上increment
ZCOUNT : ZCOUNT key-name min max ---------------------------------返回分值介于min和max之间的成员数量
ZRANK : ZRANK key-name member ------------------------------------返回成员member在有序集合中的排名
ZSCORE : ZSCORE key-name member --------------------------------返回成员member的分值
ZRANGE : ZRANGE key-name start stop [WITHSCORE] ------------返回有序集合中排名介于start和stop之间的成员,如果给定了可选的WITHSCORE选项,那么命令会将成员的分值也一并返回`
<?php
//$userid = $_SESSION['userid'];
$userid = rand(1,99999);
$goods_id = empty($_REQUEST['goods_id'])?1:$_REQUEST['goods_id'];
$goods_id = intval($goods_id);
$redis = new redis();
$redis->connect('127.0.0.1', 6379);
//设置商品数量
// $redis->set('iphone:1:num',100); die();
// print_r($redis->zrange('order:list',0,time()));die(); //返回区间成员
if((int)$redis->get('iphone:1:num') <= 0){ //设置键
echo "商品已抢完 ",$redis->get('iphone:1:num'); exit;
}
// Zrank 返回有序集中指定成员的排名。其中有序集成员按分数值递增(从小到大)顺序排列
if($redis->zrank('order:list',$userid.'|'.$goods_id) !== false ){
echo "您已经抢到过此商品";exit;
}
// echo "OK";die();
//Watch 监听一个(或多个) key 如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断
if($redis->watch("iphone:{$goods_id}:version","iphone:{$goods_id}:num") && $redis->get("iphone:{$goods_id}:num") >=1 ){
try{
$redis->multi(); //multi 标记一个事务块的开始
$redis->incr('iphone:1:version'); //incr 递增
$redis->decr('iphone:1:num'); //递减
$result = $redis->exec(); //exec执行所有事务块内的命令
if($result){
//数据库中生成订单(慢) 所以我先存入redis中
//$redis->lpush("order:list:{$goods_id}",$userid.'-'.time());
if($redis->zadd("order:list",time(),$userid.'|'.$goods_id)){ //添加有序队列
echo '抢购成功';
}else{
echo "失败";
}
}else{
var_dump($result);
}
}catch(Exception $e){
echo "失败";
}
}
$arr = $redis->zrange('order:list',0,0,' WITHSCORES');//区间返回成员
var_dump($arr);
ab -n 2000 -c 1020 http://127.0.0.1/temp/
压力测试 完全没有问题