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 相关文章推荐
解析Extjs与php数据交互(增删查改)
Jun 25 PHP
php实现的常见排序算法汇总
Sep 08 PHP
php跨服务器访问方法小结
May 12 PHP
PHP模板引擎smarty详细介绍
May 26 PHP
php基础设计模式大全(注册树模式、工厂模式、单列模式)
Aug 31 PHP
WordPress中获取页面链接和标题的相关PHP函数用法解析
Dec 17 PHP
全新Mac配置PHP开发环境教程
Feb 03 PHP
SAE实时日志接口SDK用法示例
Oct 09 PHP
关于ThinkPhp 框架表单验证及ajax验证问题
Jul 19 PHP
PHP多维数组排序array详解
Nov 21 PHP
php打开本地exe程序,js打开本地exe应用程序,并传递相关参数方法
Feb 06 PHP
使用laravel的Eloquent模型如何获取数据库的指定列
Oct 17 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
全国FM电台频率大全 - 2 天津市
2020/03/11 无线电
php文件缓存方法总结
2016/03/16 PHP
Javascript的一种模块模式
2010/09/08 Javascript
jBox 2.3基于jquery的最新多功能对话框插件 常见使用问题解答
2011/11/10 Javascript
jQuery LigerUI 使用教程表格篇(1)
2012/01/18 Javascript
JavaScript NodeTree导航栏(菜单项JSON类型/自制)
2013/02/01 Javascript
jQuery实现单击弹出Div层窗口效果(可关闭可拖动)
2015/09/19 Javascript
使用jQuery判断Div是否在可视区域的方法 判断div是否可见
2016/02/17 Javascript
BootStrap使用popover插件实现鼠标经过显示并保持显示框
2016/06/23 Javascript
原生ajax处理json格式数据的实例代码
2016/12/25 Javascript
图片上传之FileAPI与NodeJs
2017/01/24 NodeJs
在js代码拼接dom对象到页面上去的模板总结(必看)
2017/02/14 Javascript
慕课网题目之js实现抽奖系统功能
2017/09/19 Javascript
用JavaScript做简易的购物车的代码示例
2017/10/20 Javascript
使用Bootstrap + Vue.js实现表格的动态展示、新增和删除功能
2017/11/27 Javascript
Dropify.js图片宽高自适应的方法
2017/11/27 Javascript
ReactNative实现Toast的示例
2017/12/31 Javascript
基于Vue2x的图片预览插件的示例代码
2018/05/14 Javascript
angularjs模态框的使用代码实例
2019/12/20 Javascript
ES6使用 Array.includes 处理多重条件用法实例分析
2020/03/02 Javascript
微信小程序整个页面的自动适应布局的实现
2020/07/12 Javascript
[01:09]DOTA2次级职业联赛 - 99战队宣传片
2014/12/01 DOTA
[02:46]完美世界DOTA2联赛PWL DAY4集锦
2020/11/03 DOTA
python调用shell的方法
2013/11/20 Python
Python实现统计单词出现的个数
2015/05/28 Python
Python 12306抢火车票脚本 Python京东抢手机脚本
2018/02/06 Python
详解Python中where()函数的用法
2018/03/27 Python
Flask框架响应、调度方法和蓝图操作实例分析
2018/07/24 Python
Django框架中间件(Middleware)用法实例分析
2019/05/24 Python
澳大利亚首个在线预订旅游网站:Wotif
2017/07/19 全球购物
英国最受欢迎的平价女士时装零售商:Roman Originals
2019/11/02 全球购物
JYSK加拿大:购买家具、床垫、家居装饰等
2020/02/14 全球购物
人力资源部经理岗位职责规定
2014/02/23 职场文书
入党积极分子评语
2014/05/04 职场文书
企业安全标语
2014/06/07 职场文书
分享MySQL常用 内核 Debug 几种常见方法
2022/03/17 MySQL