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 相关文章推荐
在PHP中操作Excel实例代码
Apr 29 PHP
PHP中foreach循环中使用引用要注意的地方
Jan 02 PHP
php产生随机数的两种方法实例代码 输出随机IP
Apr 08 PHP
PHP setcookie指定domain参数后,在IE下设置cookie失效的解决方法
Sep 09 PHP
解析php中var_dump,var_export,print_r三个函数的区别
Jun 21 PHP
php生成缩略图填充白边(等比缩略图方案)
Dec 25 PHP
如何让CI框架支持service层
Oct 29 PHP
Yii使用DeleteAll连表删除出现报错问题的解决方法
Jul 14 PHP
在Mac OS下搭建LNMP开发环境的步骤详解
Mar 10 PHP
利用PHP获取汉字首字母并且分组排序详解
Oct 22 PHP
php实现socket推送技术的示例
Dec 20 PHP
Laravel 微信小程序后端搭建步骤详解
Nov 26 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游戏编程25个脚本代码
2011/02/08 PHP
php 的加密函数 md5,crypt,base64_encode 等使用介绍
2012/04/09 PHP
浅析php变量作用域的一些问题
2013/08/08 PHP
自定义session存储机制避免会话保持问题
2014/10/08 PHP
Ubuntu中搭建Nginx、PHP环境最简单的方法
2015/03/05 PHP
php微信公众号开发模式详解
2016/11/28 PHP
/etc/php-fpm.d/www.conf 配置注意事项
2017/02/04 PHP
php表单处理操作
2017/11/16 PHP
PHP单元测试配置与使用方法详解
2019/12/27 PHP
一个封装js代码-----展开收起效果示例
2013/07/03 Javascript
jquery统计用户选中的复选框的个数
2014/06/06 Javascript
3种Jquery限制文本框只能输入数字字母的方法
2014/12/03 Javascript
jquery ajax 如何向jsp提交表单数据
2015/08/23 Javascript
JavaScript文本框脚本编写的注意事项
2016/01/25 Javascript
javascript数据结构之多叉树经典操作示例【创建、添加、遍历、移除等】
2018/08/01 Javascript
[43:58]DOTA2-DPC中国联赛定级赛 LBZS vs SAG BO3第一场 1月8日
2021/03/11 DOTA
python使用正则表达式分析网页中的图片并进行替换的方法
2015/03/26 Python
用Python编写简单的定时器的方法
2015/05/02 Python
详谈Python中列表list,元祖tuple和numpy中的array区别
2018/04/18 Python
django+xadmin+djcelery实现后台管理定时任务
2018/08/14 Python
Python判断一个三位数是否为水仙花数的示例
2018/11/13 Python
使用PyQt4 设置TextEdit背景的方法
2019/06/14 Python
Django中的FBV和CBV用法详解
2019/09/15 Python
python是否适合网页编程详解
2019/10/04 Python
Python+OpenCV实现将图像转换为二进制格式
2020/01/09 Python
Python cookie的保存与读取、SSL讲解
2020/02/17 Python
解决Keras TensorFlow 混编中 trainable=False设置无效问题
2020/06/28 Python
使用HTML5的Notification API制作web通知的教程
2015/05/08 HTML / CSS
iRobot官网:改变生活的家用机器人品牌
2016/09/20 全球购物
The Hut英国:英国领先的豪华在线百货商店
2019/07/26 全球购物
秋季运动会稿件
2014/01/30 职场文书
聘任书模板
2014/03/29 职场文书
先进集体申报材料
2014/12/25 职场文书
党支部考察鉴定意见
2015/06/02 职场文书
素质拓展训练感想
2015/08/07 职场文书
CSS的class与id常用的命名规则
2021/05/18 HTML / CSS