** 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_exporter监听多个节点 redis 监听_有序集合


并发量大的话出现了超卖

解决 使用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/ 压力测试 完全没有问题