PHP+Redis事务解决高并发下商品超卖问题(推荐)


Posted in PHP onAugust 03, 2020

对于一些有一定用户量的电商网站,如果只是单纯的使用关系型数据库(如MySQL、Oracle)来做抢购,对数据库的压力是非常大的,而且如果不使用好数据库的锁机制,还会导致商品、优惠券超卖的问题。我所在的公司也遇到了同样的问题,问题发生在优惠券被超量抢购上,在问题发生后我们开始想办法解决问题,由于自己使用redis比较多,我准备使用redis来解决这个问题。利用redis的高性能和事务特性来解决线上优惠券被超库存抢购的问题,下面我给出我临时解决这个问题的第一版的伪代码,去掉了一些细节:

/**
 * 抢优惠券(秒杀)
 * @param int $couponId 商品ID
 * @param int $uid 用户ID
 * @return bool
 */
function secKill($couponId, $uid)
{
 //1.初始化Redis连接
 $redis = new Redis();
 if (!$redis->connect('127.0.0.1', 6379)) {
 trigger_error('Redis连接出错!!!', E_USER_ERROR);
 } else {
 echo '连接正常<br>';
 }

 //秒杀商品的库存key
 $key = 'secKill:'.$couponId.':stock';
 $redis->watch($key);

 //获取库存
 $stock = $redis->get($key);

 //秒杀未开始,表示库存为null
 if (!$stock && !is_numeric($stock)) {
 echo '秒杀未开始';
 return false;
 }

 //判断库存,如果库存大于0,则减库存,将该成功秒杀用户加入哈希表,如果小于等于0,秒杀结束
 if ($stock <= 0) {
 echo '秒杀已结束';
 return false;
 }

 //用户已经成功秒杀过一次了,不允许再次参与秒杀
 if ($redis->sIsMember('secKill:'.$couponId.':uid', $uid)) {
 echo '秒杀失败';
 return false;
 }

 //代码走到这里,说明该用户是第一次参与秒杀,将库存减一,然后把这个人放到已抢到的集合表
 //multi(),返回一个redis对象,并进入multi-mode模式,一旦进入multi-mode模式,以后调用的所有方法都会返回相同的对象,
 //直到exec()方法被调用。
 $result = $redis->multi()->decr($key)->sAdd('secKill:'.$couponId.':uid', $uid)->exec();

 if (empty($result)) {//事务被取消
 echo '秒杀失败';
 return false;
 }

 //抢券成功,将优惠券ID和UID放入到队列中,由一个单独的进程队列来消费队列里的数据,向用户推送抢到的优惠券
 $redis->lPush('couponOrder', $couponId.'+'.$uid);

 $redis->close();
 return true;
}

$couponId = 11211;
$uid = mt_rand(1, 100);
secKill($couponId, $uid);

首先,我模拟设置优惠券ID为11211的优惠券库存为10个。

PHP+Redis事务解决高并发下商品超卖问题(推荐)

然后,我们使用ab工具来模拟1000次请求,50并发量来测试

ab -n 1000 -c 50 www.test.com/

然后我们通过Redis Desktop Manager来查看一些Redis的结果

couponOrder队列里已经有了10个用户的信息了

PHP+Redis事务解决高并发下商品超卖问题(推荐)

并且优惠券的剩余数量也是0了,不再是负数了

PHP+Redis事务解决高并发下商品超卖问题(推荐)

同时,用户抢券集合里也保存了10个用户的UID信息。

PHP+Redis事务解决高并发下商品超卖问题(推荐)

上面这串代码解决了两个问题:

  • 解决了瞬时的大量查询到数据库上给数据库造成很大压力的问题,流量都被拦截在了redis缓存层
  • 解决了优惠券被超库存抢购的问题

但是,这段代码也存在一定的问题:

  1. 没有使用redis连接池,频繁创建新的redis有一定的性能影响
  2. 由于使用了事务,每一次并发请求中只会有一个用户抢券成功,该并发请求中的其它用户都会失败,只能等第二次并发
  3. 同样还是事务导致的库存遗留问题,如果有10个商品,1000次请求每次200并发量,5次并发请求就完成了1000次请求,但是只会有5个用户成功抢到,如果没有后续的请求,会导致库存还有5份存量

提示:在消费队列里,如果优惠券发放失败,一定要立即记录并短信通知运营管理人员,看看是否能重发或者通过后台手动定向推送给用户。

所以,后续我又使用了lua脚本和redis配合一起来解决了这个问题。具体代码,我会后续整理处理补充完整。

总结

到此这篇关于PHP+Redis事务解决高并发下商品超卖问题的文章就介绍到这了,更多相关php redis 解决高并发下商品超卖内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

PHP 相关文章推荐
php 正则 过滤html 的超链接
Jun 02 PHP
用PHP读取和编写XML DOM的实现代码
Feb 03 PHP
PHP实现手机归属地查询API接口实现代码
Aug 27 PHP
基于php上传图片重命名的6种解决方法的详细介绍
Apr 28 PHP
深入php-fpm的两种进程管理模式详解
Jun 03 PHP
PHP CodeBase:将时间显示为&quot;刚刚&quot;&quot;n分钟/小时前&quot;的方法详解
Jun 06 PHP
使用PHP获取当前url路径的函数以及服务器变量
Jun 29 PHP
php实现的短网址算法分享
Jun 20 PHP
PHP 常用的header头部定义汇总
Jun 19 PHP
php操作mongodb封装类与用法实例
Sep 01 PHP
PHP单例模式模拟Java Bean实现方法示例
Dec 07 PHP
PHP封装的分页类与简单用法示例
Feb 25 PHP
PHP+Redis链表解决高并发下商品超卖问题(实现原理及步骤)
Aug 03 #PHP
PHP Ajax跨域问题解决方案代码实例
Aug 01 #PHP
PHP生成图表pChart的示例解析
Jul 31 #PHP
基于php伪静态的实现方法解析
Jul 31 #PHP
PHP底层运行机制与工作原理详解
Jul 31 #PHP
如何通过Apache在本地配置多个虚拟主机
Jul 29 #PHP
PHP网页缓存技术优点及代码实例
Jul 29 #PHP
You might like
PHP Session变量不能传送到下一页的解决方法
2009/11/27 PHP
php中导出数据到excel时数字变为科学计数的解决方法
2013/02/03 PHP
浅析linux下apache服务器的配置和管理
2013/08/10 PHP
php 不使用js实现页面跳转
2014/02/11 PHP
php定界符
2014/06/19 PHP
php防止sql注入之过滤分页参数实例
2014/11/03 PHP
PHP类的特性实例分析
2016/09/28 PHP
PHP实现的字符串匹配算法示例【sunday算法】
2017/12/19 PHP
img的onload的另类用法
2008/01/10 Javascript
jquery ajax abort()的使用方法
2010/10/28 Javascript
jQuery 拖动层(在可视区域范围内)
2012/05/24 Javascript
如何使用jQuery Draggable和Droppable实现拖拽功能
2013/07/05 Javascript
js获取select标签的值且兼容IE与firefox
2013/12/30 Javascript
三种取消选中单选框radio的方法
2014/09/09 Javascript
jquery复选框多选赋值给文本框的方法
2015/01/27 Javascript
使用JavaScript的AngularJS库编写hello world的方法
2015/06/23 Javascript
Jquery基础教程之DOM操作
2015/08/19 Javascript
Jquery ajax基础教程
2015/11/20 Javascript
JS查找英文文章中出现频率最高的单词
2017/03/20 Javascript
ES6新特性之解构、参数、模块和记号用法示例
2017/04/01 Javascript
jQuery制作全屏宽度固定高度轮播图(实例讲解)
2017/07/08 jQuery
超级简易的JS计算器实例讲解(实现加减乘除)
2017/08/08 Javascript
vue 表单验证按钮事件交由父组件触发的方法
2018/12/17 Javascript
vue实现瀑布流组件滑动加载更多
2020/03/10 Javascript
[04:31]2016国际邀请赛中国区预选赛妖精采访
2016/06/27 DOTA
Python守护进程(daemon)代码实例
2015/03/06 Python
Python中的异常处理相关语句基础学习笔记
2016/07/11 Python
Python 3.8 新功能大揭秘【新手必学】
2020/02/05 Python
python request 模块详细介绍
2020/11/10 Python
python+playwright微软自动化工具的使用
2021/02/02 Python
Sam’s Club山姆会员商店:沃尔玛旗下高端会员制商店
2017/01/16 全球购物
具结保证书
2015/01/17 职场文书
写给老婆的保证书
2015/02/27 职场文书
工作年限证明模板
2015/06/15 职场文书
2019入党申请书格式
2019/06/25 职场文书
go语言中json数据的读取和写出操作
2021/04/28 Golang