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 相关文章推荐
PHP中simplexml_load_string函数使用说明
Jan 01 PHP
最新用php获取谷歌PR值算法,附上php查询PR值代码示例
Dec 25 PHP
解析PHP获取当前网址及域名的实现代码
Jun 23 PHP
php微信公众开发之获取周边酒店信息的方法
Dec 22 PHP
PHP开发注意事项总结
Feb 04 PHP
php将图片保存为不同尺寸图片的图片类实例
Mar 30 PHP
thinkPHP框架实现图像裁剪、缩放、加水印的方法
Mar 14 PHP
PHP回调函数概念与用法实例分析
Nov 03 PHP
PHP判断是否是微信打开,浏览器打开的方法
Mar 14 PHP
PHP实现基于PDO扩展连接PostgreSQL对象关系数据库示例
Mar 31 PHP
PHP swoole和redis异步任务实现方法分析
Aug 12 PHP
php快速导入大量数据的实例方法
Sep 23 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 编写的日历
2006/10/09 PHP
探讨:parse url解析URL,返回其组成部分
2013/06/14 PHP
PHP中overload与override的区别
2017/02/13 PHP
PHP进阶学习之命名空间基本用法分析
2019/06/18 PHP
Laravel手动返回错误码示例
2019/10/22 PHP
PHP+Redis链表解决高并发下商品超卖问题(实现原理及步骤)
2020/08/03 PHP
网上应用的一个不错common.js脚本
2007/08/08 Javascript
JavaScript DOM 学习第九章 选取范围的介绍
2010/02/19 Javascript
Jquery多选框互相内容交换的实例代码
2013/07/04 Javascript
jquery对元素拖动排序示例
2014/01/16 Javascript
解决同一页面中两个iframe互相调用jquery,js函数的方法
2016/12/12 Javascript
Jquery树插件zTree实现菜单树
2017/01/24 Javascript
angular6.x中ngTemplateOutlet指令的使用示例
2018/08/09 Javascript
jQuery+CSS实现的标签页效果示例【测试可用】
2018/08/14 jQuery
详解auto-vue-file:一个自动创建vue组件的包
2019/04/26 Javascript
js实现飞机大战游戏
2020/08/26 Javascript
vue组件添加事件@click.native操作
2020/10/30 Javascript
[47:26]完美世界DOTA2联赛 LBZS vs Forest 第二场 11.07
2020/11/09 DOTA
python中精确输出JSON浮点数的方法
2014/04/18 Python
python错误:AttributeError: 'module' object has no attribute 'setdefaultencoding'问题的解决方法
2014/08/22 Python
给Python入门者的一些编程建议
2015/06/15 Python
Python与人工神经网络:使用神经网络识别手写图像介绍
2017/12/19 Python
Python模块的加载讲解
2019/01/15 Python
Python开发网站目录扫描器的实现
2019/02/21 Python
PyCharm中代码字体大小调整方法
2019/07/29 Python
Windows10下Tensorflow2.0 安装及环境配置教程(图文)
2019/11/21 Python
python 实现仿微信聊天时间格式化显示的代码
2020/04/17 Python
你正在寻找的CSS3 动画技术
2011/07/27 HTML / CSS
浅谈html5之sse服务器发送事件EventSource介绍
2017/08/28 HTML / CSS
Photobook澳大利亚:制作相片书,婚礼卡,旅行相簿
2017/01/12 全球购物
英国蜡烛、蜡烛配件和家居香氛购买网站:Yankee Candle
2018/12/12 全球购物
Paper Cape官网:美国婴儿和儿童服装品牌
2019/11/02 全球购物
华为的Java面试题
2014/03/07 面试题
员工辞退通知书
2015/04/17 职场文书
幼儿园园长六一致辞
2015/07/31 职场文书
2016年大学光棍节活动总结
2016/04/05 职场文书