ThinkPHP5.1结合Redis模拟秒杀(悲观锁,乐观锁)

ThinkPHP5.1结合Redis模拟秒杀(悲观锁,乐观锁)

test34()初始化Redis扣库存测试数据

  • 每次进行并发测试前,都要执行的初始化

test35无事务,无锁

  • 超卖

test36有事务,无锁

  • 超卖

test37有事务,watch乐观锁

  • 不超卖 数据凌乱,适用读多写少情况

test38有事务,setnx分布式锁

  • 不超卖 数据整齐,适用写多读少情况

具体代码如下

    public function test34()
    {
        Redis::set('stock', 10000);
        Redis::del('queue');
        //以上两行代码,为不加锁代码,并发情况下,抢到相同库存号;
        Redis::set('stock_transaction', 50);
        Redis::del('queue_transaction');
        Redis::del('exec');
        Redis::set('count',0);//判定大于零次数
        Redis::set('buyer',0);//买家个数
        Redis::del('lock:stock');//释放锁
        echo '初始化Redis扣库存测试数据
'
; echo 'test35无事务,无锁
'
; echo 'test36有事务,无锁
'
; echo 'test37有事务,watch乐观锁
'
; echo 'test38有事务,setnx分布式锁
'
; } //抢库存 无锁 并发情况下,抢到相同库存号; public function test35() { $stock = Redis::get('stock'); Redis::decr('stock'); Redis::lpush('queue', $stock); echo "抢到第 $stock 个"; } //抢库存,事务,无锁,超卖了 /** * 注意,Redis事务中,不能和PHP进行交互 但是可以exec后,会返回事务中各条命令的值 * * 命令错误 事务全部命令不执行 * 命令操作对象错误 事务全部命令执行 */ public function test36() { Redis::incr('buyer');//买家个数 $buy=1;//循环标志 do { if (Redis::get('stock_transaction')>0){ Redis::incr('count');//判定大于零次数 usleep(rand(0,100000));//给时间给别人改 Redis::multi(); Redis::decr('stock_transaction');//扣库存 //返回扣库存后的值 Redis::lpush('queue_transaction',1);//用于统计执行次数,实际业务可以用于存放Uid 返回内部元素个数 $ok=Redis::exec();//返回事务内,所有命令的返回值,按命令先后排序,当操作别打断时,返回空 if (is_array($ok)){ Redis::lpush('exec',json_encode($ok)); echo '买到了第'.($ok[0]+1).'个'; $buy=0; }else{ usleep(rand(0,100000)); } //这里要解决被别人改掉以后,重试问题 }else{ echo '已经销售一空了'; $buy=0; } }while($buy); } //watch锁加事务 不会超卖 乐观锁,适用于读多写少的情况 得到的响应数据,比较凌乱 public function test37(){ Redis::incr('buyer');//买家个数 $buy=1;//循环标志 do { Redis::watch('stock_transaction'); if (Redis::get('stock_transaction')>0){ Redis::incr('count');//判定大于零次数 usleep(rand(0,100000));//给时间给别人改 Redis::multi(); Redis::decr('stock_transaction');//扣库存 //返回扣库存后的值 Redis::lpush('queue_transaction',1);//用于统计执行次数,实际业务可以用于存放Uid 返回内部元素个数 $ok=Redis::exec();//返回事务内,所有命令的返回值,按命令先后排序,当操作别打断时,返回空 if (is_array($ok)){ Redis::lpush('exec',json_encode($ok)); echo '买到了第'.($ok[0]+1).'个'; $buy=0; }else{ usleep(rand(0,100000)); } //这里要解决被别人改掉以后,重试问题 }else{ echo '已经销售一空了'; $buy=0; } }while($buy); } //redis 悲观锁模拟抢购,适用于写多,读少的情况 悲观锁,得到的响应数据,很整齐划一 public function test38(){ Redis::incr('buyer');//买家个数 $buy=1;//循环标志 do { if ($this->redisLock() and Redis::get('stock_transaction')>0){//注意,这里的获取锁,需要放到前面,否则超卖 Redis::incr('count');//判定大于零次数 usleep(rand(0,100000));//给时间给别人改 Redis::multi(); Redis::decr('stock_transaction');//扣库存 //返回扣库存后的值 Redis::lpush('queue_transaction',1);//用于统计执行次数,实际业务可以用于存放Uid 返回内部元素个数 $ok=Redis::exec();//返回事务内,所有命令的返回值,按命令先后排序,当操作别打断时,返回空 if (is_array($ok)){ Redis::lpush('exec',json_encode($ok)); echo '买到了第'.($ok[0]+1).'个'; $buy=0; }else{ usleep(rand(0,100000)); } //这里要解决被别人改掉以后,重试问题 }else{ echo '已经销售一空了'; $buy=0; } $this->redisUnlock(); }while($buy); } //redis 库存实验获得锁 public function redisLock(){ do{ $lock=Redis::setnx('lock:stock',1);//不存在,执行操作,返回1,即上锁 ,存在则不操作,返回0; if ($lock){ Redis::expire('lock:stock',60);//60s过期 防止死锁 return true; }else{ usleep(rand(0,100000)); } }while(1); } //redis 库存实验解除锁 public function redisUnlock(){ Redis::del('lock:stock'); }

你可能感兴趣的:(学习验证类)