PHP+redis实现的限制抢购防止商品超发功能详解


Posted in PHP onSeptember 19, 2019

本文实例讲述了PHP+redis实现的限制抢购防止商品超发功能。分享给大家供大家参考,具体如下:

  • redis不仅仅是单纯的缓存,它还有一些特殊的功能,在一些特殊场景上很好用。redis中key的原子自增incrby和判断key不存在再写入的setnx方法,可以有效的防止超发。
  • 下面使用两个不同的方式来说明利用redis做商品购买库存数量限制。
  • 业务场景很简单,就是限制抢购5个商品,模拟并发请求抢购商品,每抢购一次对应redis中的key值增加一次,通过判断限购的数量来限制抢购,抢购成功写入成功日志,失败写入失败的信息记录,通过记录的数量来判断是否超发。

文件index.php

<?php
require_once './myRedis.php';
require_once './function.php';
class sendAward{
  public $conf = [];
  const V1 = 'way1';//版本一
  const V2 = 'way2';//版本二
  const AMOUNTLIMIT = 5;//抢购数量限制
  const INCRAMOUNT = 1;//redis递增数量值
  //初始化调用对应方法执行商品发放
  public function __construct($conf,$type){
    $this->conf = $conf;
    if(empty($type))
      return '';
    if($type==self::V1){
      $this->way1(self::V1);
    }elseif($type==self::V2){
      $this->way2(self::V2);
    }else{
      return '';
    }
  }
  //抢购商品方式一
  protected function way1($v){
    $redis = new myRedis($this->conf);   
    $keyNmae = getKeyName($v);
    if(!$redis->exists($keyNmae)){
      $redis->set($keyNmae,0);
    }
    $currAmount = $redis->get($keyNmae);
    if(($currAmount+self::INCRAMOUNT)>self::AMOUNTLIMIT){
      writeLog("没有抢到商品",$v);
      return;
    }
    $redis->incrby($keyNmae,self::INCRAMOUNT);
    writeLog("抢到商品",$v);
  }
  //抢购商品方式二
  protected function way2($v){
    $redis = new myRedis($this->conf);
    $keyNmae = getKeyName($v);
    if(!$redis->exists($keyNmae)){
      $redis->setnx($keyNmae,0);
    }
    if($redis->incrby($keyNmae,self::INCRAMOUNT) > self::AMOUNTLIMIT){
      writeLog("没有抢到商品",$v);
      return;
    }
    writeLog("抢到商品",$v);
  }
}
//实例化调用对应执行方法
$type = isset($_GET['v'])?$_GET['v']:'way1';
$conf = [
  'host'=>'192.168.0.214','port'=>'6379',
  'auth'=>'test','db'=>2,
];
new sendAward($conf,$type);

文件myRedis.php

<?php
/**
 * @desc 自定义redis操作类
 * **/
class myRedis{
  public $handler = NULL;
  public function __construct($conf){
    $this->handler = new Redis();
    $this->handler->connect($conf['host'], $conf['port']); //连接Redis
    //设置密码
    if(isset($conf['auth'])){
      $this->handler->auth($conf['auth']); //密码验证
    }
    //选择数据库
    if(isset($conf['db'])){
      $this->handler->select($conf['db']);//选择数据库2
    }else{
      $this->handler->select(0);//默认选择0库
    }
  }
  //获取key的值
  public function get($name){
    return $this->handler->get($name);
  }
  //设置key的值
  public function set($name,$value){
    return $this->handler->set($name,$value);
  }
  //判断key是否存在
  public function exists($key){
    if($this->handler->exists($key)){
      return true;
    }
    return false;
  }
  //当key不存在的设置key的值,存在则不设置
  public function setnx($key,$value){
    return $this->handler->setnx($key,$value);
  }
  //将key的数值增加指定数值
  public function incrby($key,$value){
    return $this->handler->incrBy($key,$value);
  }
}

文件function.php

<?php
//获取商品key名称
function getKeyName($v)
{
  return "send_goods_".$v;
}
//日志写入方法
function writeLog($msg,$v)
{
  $log = $msg.PHP_EOL;
  file_put_contents("log/$v.log",$log,FILE_APPEND);
}

1.ab工具并发测试way1方法

[root@localhost oversend]# ab -c 100 -n 200 http://192.168.0.213:8083/index.php?v=way1
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 192.168.0.213 (be patient)
Completed 100 requests
Completed 200 requests
Finished 200 requests
Server Software:    nginx
Server Hostname:    192.168.0.213
Server Port:      8083
Document Path:     /index.php?v=way1
Document Length:    0 bytes
Concurrency Level:   100
Time taken for tests:  0.089 seconds
Complete requests:   200
Failed requests:    0
Write errors:      0
Total transferred:   30600 bytes
HTML transferred:    0 bytes
Requests per second:  2243.13 [#/sec] (mean)
Time per request:    44.581 [ms] (mean)
Time per request:    0.446 [ms] (mean, across all concurrent requests)
Transfer rate:     335.16 [Kbytes/sec] received
Connection Times (ms)
       min mean[+/-sd] median  max
Connect:    0  6  2.2   5   17
Processing:   2  28 16.3   25   55
Waiting:    1  26 15.2   24   50
Total:     5  34 16.3   30   60
Percentage of the requests served within a certain time (ms)
 50%   30
 66%   35
 75%   54
 80%   56
 90%   57
 95%   60
 98%   60
 99%   60
 100%   60 (longest request)

v1方法日志分析

[root@localhost log]# less -N way1.log 
   1 抢到商品
   2 抢到商品
   3 抢到商品
   4 抢到商品
   5 抢到商品
   6 抢到商品
   7 没有抢到商品
   8 没有抢到商品
   9 没有抢到商品
   10 没有抢到商品
   11 没有抢到商品
   12 没有抢到商品

观察日志发现 抢到商品的记录有6条超过正常的5条,说明超发了

2.ab工具并发测试way2方法

[root@localhost oversend]# ab -c 100 -n 200 http://192.168.0.213:8083/index.php?v=way2
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 192.168.0.213 (be patient)
Completed 100 requests
Completed 200 requests
Finished 200 requests
Server Software:    nginx
Server Hostname:    192.168.0.213
Server Port:      8083
Document Path:     /index.php?v=way2
Document Length:    0 bytes
Concurrency Level:   100
Time taken for tests:  0.087 seconds
Complete requests:   200
Failed requests:    0
Write errors:      0
Total transferred:   31059 bytes
HTML transferred:    0 bytes
Requests per second:  2311.68 [#/sec] (mean)
Time per request:    43.259 [ms] (mean)
Time per request:    0.433 [ms] (mean, across all concurrent requests)
Transfer rate:     350.58 [Kbytes/sec] received
Connection Times (ms)
       min mean[+/-sd] median  max
Connect:    0  6  5.4   5   13
Processing:   3  31 16.6   30   70
Waiting:    1  30 16.6   30   70
Total:     5  37 18.5   32   82
Percentage of the requests served within a certain time (ms)
 50%   32
 66%   41
 75%   45
 80%   50
 90%   68
 95%   80
 98%   81
 99%   82
 100%   82 (longest request)

v2方法日志分析

[root@localhost log]# less -N v2.log 
[root@localhost log]# less -N way2.log 
   1 抢到商品
   2 抢到商品
   3 抢到商品
   4 抢到商品
   5 没有抢到商品
   6 抢到商品
   7 没有抢到商品
   8 没有抢到商品
   9 没有抢到商品
   10 没有抢到商品

总结:观察日志可知抢到商品的日志记录是5条并没有超发,说明利用这种方式可以限制住库存的数量。之所以超发是因为方法一中通过加法来判断限制条件的同时,并发一大,就会越过这个判断条件出现会超发,redis的在这方面就体现优势了。

完整代码github地址

希望本文所述对大家PHP程序设计有所帮助。

PHP 相关文章推荐
用Flash图形化数据(二)
Oct 09 PHP
php实现的仿阿里巴巴实现同类产品翻页
Dec 11 PHP
使用JSON实现数据的跨域传输的php代码
Dec 20 PHP
PHP json格式和js json格式 js跨域调用实现代码
Sep 08 PHP
用PHP实现弹出消息提示框的两种方法
Dec 17 PHP
浅谈PHP解析URL函数parse_url和parse_str
Nov 11 PHP
php基于GD库画五星红旗的方法
Feb 24 PHP
PHP代码优化技巧小结
Sep 29 PHP
Zend Framework教程之配置文件application.ini解析
Mar 10 PHP
thinkPHP多语言切换设置方法详解
Nov 11 PHP
CI框架表单验证实例详解
Nov 21 PHP
php-fpm服务启动脚本的方法
Apr 27 PHP
php+redis实现消息队列功能示例
Sep 19 #PHP
php文件包含的几种方式总结
Sep 19 #PHP
smarty模板的使用方法实例分析
Sep 18 #PHP
PHP MVC框架中类的自动加载机制实例分析
Sep 18 #PHP
PHP切割整数工具类似微信红包金额分配的思路详解
Sep 18 #PHP
php实现多站点共用session实现单点登录的方法详解
Sep 18 #PHP
PHP实现批量修改文件名的方法示例
Sep 18 #PHP
You might like
php生成随机密码的三种方法小结
2010/09/04 PHP
PHP 简易输出CSV表格文件的方法详解
2013/06/20 PHP
php使用curl打开https网站的方法
2015/06/17 PHP
解决PHP里大量数据循环时内存耗尽的方法
2015/10/10 PHP
利用Fix Rss Feeds插件修复WordPress的Feed显示错误
2015/12/19 PHP
浅谈重写window对象的方法
2014/12/29 Javascript
javascript制作sql转换为stringBuffer的小工具
2015/04/03 Javascript
jQuery实现鼠标选中文字后弹出提示窗口效果【附demo源码】
2016/09/05 Javascript
常用原生js自定义函数总结
2016/11/20 Javascript
JavaScript实现自定义媒体播放器方法介绍
2017/01/03 Javascript
详解node-ccap模块生成captcha验证码
2017/07/01 Javascript
JS继承与闭包及JS实现继承的三种方式
2017/10/15 Javascript
vue.js  父向子组件传参的实例代码
2017/10/29 Javascript
微信端调取相册和摄像头功能,实现图片上传,并上传到服务器
2019/05/16 Javascript
微信小程序之几种常见的弹框提示信息实现详解
2019/07/11 Javascript
一个手写的vue放大镜效果
2019/08/09 Javascript
Fabric 应用案例
2016/08/28 Python
Python实现购物系统(示例讲解)
2017/09/13 Python
Django使用httpresponse返回用户头像实例代码
2018/01/26 Python
Django 使用Ajax进行前后台交互的示例讲解
2018/05/28 Python
Python 使用folium绘制leaflet地图的实现方法
2019/07/05 Python
python字符串切割:str.split()与re.split()的对比分析
2019/07/16 Python
Python爬虫 批量爬取下载抖音视频代码实例
2019/08/16 Python
python绘制BA无标度网络示例代码
2019/11/21 Python
python FTP批量下载/删除/上传实例
2019/12/22 Python
pytorch查看torch.Tensor和model是否在CUDA上的实例
2020/01/03 Python
使用 PyTorch 实现 MLP 并在 MNIST 数据集上验证方式
2020/01/08 Python
python实时监控logstash日志代码
2020/04/27 Python
美国在线健康和美容市场:Pharmapacks
2018/12/05 全球购物
军训考核自我鉴定
2014/02/13 职场文书
绘画专业自荐信
2014/07/04 职场文书
2014年群众路线教育实践活动整改措施
2014/09/24 职场文书
市场营销计划书
2015/01/17 职场文书
《我的伯父鲁迅先生》教学反思
2016/02/16 职场文书
公司开业的祝贺语大全(60条)
2019/07/05 职场文书
redis复制有可能碰到的问题汇总
2022/04/03 Redis