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之第五天
Oct 09 PHP
基于PHP的cURL快速入门教程 (小偷采集程序)
Jun 02 PHP
php读取图片内容并输出到浏览器的实现代码
Aug 08 PHP
PHP中常用的输出函数总结
Sep 22 PHP
PHP+jquery+ajax实现即时聊天功能实例
Dec 23 PHP
PHP实现过滤各种HTML标签
May 17 PHP
PHP 数组基本操作方法详解
Jun 17 PHP
Yii2.0预定义的别名功能小结
Jul 04 PHP
ThinkPHP实现登录退出功能
Jun 29 PHP
tp5(thinkPHP5)操作mongoDB数据库的方法
Jan 20 PHP
PHP实现微信对账单处理
Oct 01 PHP
PHP实现通过文本文件统计页面访问量功能示例
Feb 13 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
Windows PHP5和Apache的安装与配置
2009/06/08 PHP
在laravel中使用with实现动态添加where条件
2019/10/10 PHP
PHP发送邮件确认验证注册功能示例【修改别人邮件类】
2019/11/09 PHP
window.open()弹出居中的窗口
2007/02/01 Javascript
JavaScript var声明变量背后的原理示例解析
2013/10/12 Javascript
jquery中push()的用法(数组添加元素)
2014/11/25 Javascript
js中遍历Map对象的方法
2016/07/27 Javascript
Bootstrap基本组件学习笔记之分页(12)
2016/12/08 Javascript
jquery获取下拉框中的循环值
2017/02/08 Javascript
JS HTML图片显示Canvas 压缩功能
2017/07/21 Javascript
基于JS实现移动端左滑删除功能
2017/07/28 Javascript
node实现基于token的身份验证
2018/04/09 Javascript
element-ui多文件上传的实现示例
2019/04/10 Javascript
JavaScript Event Loop相关原理解析
2020/06/10 Javascript
在vue中created、mounted等方法使用小结
2020/07/21 Javascript
详解node.js创建一个web服务器(Server)的详细步骤
2021/01/15 Javascript
Python判断Abundant Number的方法
2015/06/15 Python
Python调用ctypes使用C函数printf的方法
2017/08/23 Python
python分治法求二维数组局部峰值方法
2018/04/03 Python
Python错误处理操作示例
2018/07/18 Python
用python爬取租房网站信息的代码
2018/12/14 Python
python set内置函数的具体使用
2019/07/02 Python
django中上传图片分页三级联动效果的实现代码
2019/08/30 Python
Django框架安装方法图文详解
2019/11/04 Python
python tornado修改log输出方式
2019/11/18 Python
python3 os进行嵌套操作的实例讲解
2020/11/19 Python
python 如何在测试中使用 Mock
2021/03/01 Python
HTML5之WebGL 3D概述(下)—借助类库开发及框架介绍
2013/01/31 HTML / CSS
HTML5无刷新改变当前url的代码
2017/03/15 HTML / CSS
Staples加拿大官方网站:办公用品一站式采购
2016/09/25 全球购物
英国最大的户外商店:Go Outdoors
2019/04/17 全球购物
建筑工地门卫岗位职责
2014/04/30 职场文书
《圆明园的毁灭》教学反思
2016/02/16 职场文书
剧场版《转生恶役只好拔除破灭旗标》公开最新视觉图 2023年上映
2022/04/02 日漫
Go 内联优化让程序员爱不释手
2022/06/21 Golang
windows10声卡驱动怎么安装?win10声卡驱动安装操作步骤教程
2022/08/05 数码科技