php和redis实现秒杀活动的流程


Posted in PHP onJuly 17, 2019

1 说明

前段时间面试的时候,一直被问到如何设计一个秒杀活动,但是无奈没有此方面的实际经验,所以只好凭着自己的理解和一些资料去设计这么一个程序

主要利用到了redis的string和set,string主要是利用它的k-v结构去对库存进行处理,也可以用list的数据结构来处理商品的库存,set则用来确保用户进行重复的提交

其中我们最主要解决的问题是

-防止并发产生超抢/超卖

2 流程设计

php和redis实现秒杀活动的流程

3 代码

3.1 服务端代码

class MiaoSha{

 const MSG_REPEAT_USER = '请勿重复参与';
 const MSG_EMPTY_STOCK = '库存不足';
 const MSG_KEY_NOT_EXIST = 'key不存在';

 const IP_POOL = 'ip_pool';
 const USER_POOL = 'user_pool';

 /** @var Redis */
 public $redis;
 public $key;

 public function __construct($key = '')
 {
  $this->checkKey($key);
  $this->redis = new Redis(); //todo 连接池
  $this->redis->connect('127.0.0.1');
 }

 public function checkKey($key = '')
 {
  if(!$key) {
   throw new Exception(self::MSG_KEY_NOT_EXIST);
  } else {
   $this->key = $key;
  }
 }

 public function setStock($value = 0)
 {
  if($this->redis->exists($this->key) == 0) {
   $this->redis->set($this->key,$value);
  }
 }

 public function checkIp($ip = 0)
 {
  $sKey = $this->key . self::IP_POOL;
  if(!$ip || $this->redis->sIsMember($sKey,$ip)) {
   throw new Exception(self::MSG_REPEAT_USER);
  }
 }

 public function checkUser($user = 0)
 {
  $sKey = $this->key . self::USER_POOL;
  if(!$user || $this->redis->sIsMember($sKey,$user)) {
   throw new Exception(self::MSG_REPEAT_USER);
  }
 }

 public function checkStock($user = 0, $ip = 0)
 {
  $num = $this->redis->decr($this->key);
  if($num < 0 ) {
   throw new Exception(self::MSG_EMPTY_STOCK);
  } else {
   $this->redis->sAdd($this->key . self::USER_POOL, $user);
   $this->redis->sAdd($this->key . self::IP_POOL, $ip);
   //todo add to mysql
   echo 'success' . PHP_EOL;
   error_log('success' . $user . PHP_EOL,3,'/var/www/html/demo/log/debug.log');
  }
 }

 /**
  * @note:此种做法不能防止并发
  * @func checkStockFail
  * @param int $user
  * @param int $ip
  * @throws Exception
  */
 public function checkStockFail($user = 0,$ip = 0) {
  $num = $this->redis->get($this->key);
  if($num > 0 ){
   $this->redis->sAdd($this->key . self::USER_POOL, $user);
   $this->redis->sAdd($this->key . self::IP_POOL, $ip);
   //todo add to mysql
   echo 'success' . PHP_EOL;
   error_log('success' . $user . PHP_EOL,3,'/var/www/html/demo/log/debug.log');
   $num--;
   $this->redis->set($this->key,$num);
  } else {
   throw new Exception(self::MSG_EMPTY_STOCK);
  }
 }
}

3.2 客户端测试代码

function test()
{
 try{
  $key = 'cup_';
  $handler = new MiaoSha($key);
  $handler->setStock(10);
  $user = rand(1,10000);
  $ip = $user;
  $handler->checkIp($ip);
  $handler->checkUser($user);
  $handler->checkStock($user,$ip);
 } catch (\Exception $e) {
  echo $e->getMessage() . PHP_EOL;
  error_log('fail' . $e->getMessage() .PHP_EOL,3,'/var/www/html/demo/log/debug.log');
 }
}

function test2()
{
 try{
  $key = 'cup_';
  $handler = new MiaoSha($key);
  $handler->setStock(10);
  $user = rand(1,10000);
  $ip = $user;
  $handler->checkIp($ip);
  $handler->checkUser($user);
  $handler->checkStockFail($user,$ip); //不能防止并发的
 } catch (\Exception $e) {
  echo $e->getMessage() . PHP_EOL;
  error_log('fail' . $e->getMessage() .PHP_EOL,3,'/var/www/html/demo/log/debug.log');
 }
}

4 测试

测试环境说明

  • ubantu16.04
  • redis2.8.4
  • php5.5

在服务端代码里面我们有两个函数分别是checkStock和checkStockFail,其中checkStockFail不能在高并发的情况下效果很差,不能在redis层面保证库存为0的时候终止操作。

我们利用ab工具进行测试

其中 www.hello.com 是配置的虚拟主机名称 flash-sale.php 是我们脚本的名称

#第1种情况 500并发下 用客户端的test2()去执行
 ab -n 500 -c 100 www.hello.com/flash-sale.php

log日志的记录结果:

php和redis实现秒杀活动的流程

#第2种情况 5000并发下 用客户端的test2()去执行
 ab -n 5000 -c 1000 www.hello.com/flash-sale.php

log日志的记录结果:

php和redis实现秒杀活动的流程

#第3种情况 500并发下 用客户端的test()去执行
 ab -n 500 -c 100 www.hello.com/flash-sale.php

log日志的记录结果:

php和redis实现秒杀活动的流程

#第4种情况 5000并发下 用客户端的test()去执行
 ab -n 5000 -c 1000 www.hello.com/flash-sale.php

log日志的记录结果:

php和redis实现秒杀活动的流程

5 总结

我们从日志中可以很明显的看出第3、4中情况下,可以保证商品的数量总是我们设置的库存值10,但是在情况1、2下,则产生了超卖的现象

redis来控制并发主要是利用了其api都是原子性操作的优势,从checkStock和checkStockFail中可以看出,一个是直接decr对库存进行减一操作,所以不存在并发的情况,但是另一个方法是将库存值先取出做减一操作然后再重新赋值,这样的话,在并发下,多个进程会读取到多个库存为1的值,因此会产生超卖的情况

以上所述是小编给大家介绍的php和redis实现秒杀活动的流程,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

PHP 相关文章推荐
function.inc.php超越php
Dec 09 PHP
php采集速度探究总结(原创)
Apr 18 PHP
php中文乱码怎么办如何让浏览器自动识别utf-8
Jan 15 PHP
ThinkPHP权限认证Auth实例详解
Jul 22 PHP
PHP常用处理静态操作类
Apr 03 PHP
php中switch语句用法详解
Aug 17 PHP
php中让人头疼的浮点数运算分析
Oct 10 PHP
PHP实现基于图的深度优先遍历输出1,2,3...n的全排列功能
Nov 10 PHP
Laravel配置全局公共函数的方法步骤
May 09 PHP
Yii2框架加载css和js文件的方法分析
May 25 PHP
php时间戳转换代码详解
Aug 04 PHP
PHP架构及原理知识点详解
Dec 22 PHP
php web环境和命令行环境下查找php.ini的位置
Jul 17 #PHP
php命名空间设计思想、用法与缺点分析
Jul 17 #PHP
php和C#的yield迭代器实现方法对比分析
Jul 17 #PHP
php基于协程实现异步的方法分析
Jul 17 #PHP
php学习笔记之字符串常见操作总结
Jul 16 #PHP
thinkPHP+mysql+ajax实现的仿百度一下即时搜索效果详解
Jul 15 #PHP
[原创]PHP global全局变量经典应用与注意事项分析【附$GLOBALS用法对比】
Jul 12 #PHP
You might like
十天学会php(1)
2006/10/09 PHP
模拟OICQ的实现思路和核心程序(三)
2006/10/09 PHP
一周让你学会PHP 不错的学习资料
2009/02/06 PHP
destoon实现调用热门关键字的方法
2014/07/15 PHP
php文件操作相关类实例
2015/06/18 PHP
PDO::prepare讲解
2019/01/29 PHP
javascript知识点收藏
2007/02/22 Javascript
用JS判断IE版本的代码 超管用!
2011/08/09 Javascript
jQuery 文本框得失焦点的简单实例
2014/02/19 Javascript
JS使用replace()方法和正则表达式进行字符串的搜索与替换实例
2014/04/10 Javascript
js获取指定日期周数以及星期几的小例子
2014/06/27 Javascript
javascipt:filter过滤介绍及使用
2014/09/10 Javascript
jQuery实现大转盘抽奖活动仿QQ音乐代码分享
2015/08/21 Javascript
localStorage实现便签小程序
2016/11/28 Javascript
JS XMLHttpRequest原理与使用方法深入详解
2020/04/30 Javascript
vue+Element中table表格实现可编辑(select下拉框)
2020/05/21 Javascript
Python中的条件判断语句与循环语句用法小结
2016/03/21 Python
详解Python开发中如何使用Hook技巧
2017/11/01 Python
用不到50行的Python代码构建最小的区块链
2017/11/16 Python
pandas dataframe的合并实现(append, merge, concat)
2019/06/24 Python
django rest framework vue 实现用户登录详解
2019/07/29 Python
python智联招聘爬虫并导入到excel代码实例
2019/09/09 Python
Python函数的默认参数设计示例详解
2019/12/01 Python
python实现根据给定坐标点生成多边形mask的例子
2020/02/18 Python
pycharm进入时每次都是insert模式的解决方式
2021/02/05 Python
html5 offlline 缓存使用示例
2013/06/24 HTML / CSS
Feelunique中文官网:欧洲最大化妆品零售电商
2020/07/10 全球购物
聚网科技C++面试笔试题
2015/09/01 面试题
奖学金自我鉴定范文
2013/10/03 职场文书
电子商务应届生求职信
2013/11/16 职场文书
高中自我鉴定
2013/12/20 职场文书
2014世界杯球队球队口号
2014/06/05 职场文书
读书笔记格式
2015/07/02 职场文书
幼儿园开学温馨提示
2015/07/15 职场文书
2016年中学清明节活动总结
2016/04/01 职场文书
漫画「请问您今天要来点兔子吗?」最新杂志彩页公开
2022/03/24 日漫