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 相关文章推荐
CodeIgniter php mvc框架 中国网站
May 26 PHP
php模拟js函数unescape的函数代码
Oct 20 PHP
PHP图片等比例缩放生成缩略图函数分享
Jun 10 PHP
PHP的引用详解
Feb 22 PHP
PHP抓取及分析网页的方法详解
Apr 26 PHP
Yii列表定义与使用分页方法小结(3种方法)
Jul 15 PHP
php连接微软MSSQL(sql server)完全攻略
Nov 27 PHP
PHP实现用户异地登录提醒功能的方法【基于thinkPHP框架】
Mar 15 PHP
php 字符串中是否包含指定字符串的多种方法
Apr 12 PHP
PHP共享内存使用与信号控制实例分析
May 09 PHP
Laravel路由研究之domain解决多域名问题的方法示例
Apr 04 PHP
Yii框架 session 数据库存储操作方法示例
Nov 18 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
收音机另类DIY - 纸巾盒做外壳
2021/03/02 无线电
php用正则表达式匹配URL的简单方法
2013/11/12 PHP
Nginx下ThinkPHP5的配置方法详解
2017/08/01 PHP
JavaScript基本对象
2007/01/11 Javascript
javascript 放大镜 v1.0 基于Yui2 实现的放大镜效果
2010/03/08 Javascript
基于MooTools的很有创意的滚动条时钟动画
2010/11/14 Javascript
jQuery插件的写法分享
2013/06/12 Javascript
Javascript中call和apply函数的比较和使用实例
2015/02/03 Javascript
JavaScript中textRange对象使用方法小结
2015/03/24 Javascript
jQuery使用deferreds串行多个ajax请求
2016/08/22 Javascript
JS实现的手机端精简幻灯片效果
2016/09/05 Javascript
Html5 js实现手风琴效果
2020/04/17 Javascript
js 获取今天以及过去日期
2017/04/11 Javascript
前端html中jQuery实现对文本的搜索功能并把搜索相关内容显示出来
2017/11/14 jQuery
对vue2.0中.vue文件页面跳转之.$router.push的用法详解
2018/08/24 Javascript
JS中间件设计模式的深入探讨与实例分析
2020/04/11 Javascript
Element InfiniteScroll无限滚动的具体使用方法
2020/07/27 Javascript
利用Anaconda完美解决Python 2与python 3的共存问题
2017/05/25 Python
Python sorted函数详解(高级篇)
2018/09/18 Python
Python实现的大数据分析操作系统日志功能示例
2019/02/11 Python
python自动化实现登录获取图片验证码功能
2019/11/20 Python
如何基于Python实现电子邮件的发送
2019/12/16 Python
pytorch查看torch.Tensor和model是否在CUDA上的实例
2020/01/03 Python
tensorflow使用freeze_graph.py将ckpt转为pb文件的方法
2020/04/22 Python
基于Python快速处理PDF表格数据
2020/06/03 Python
Python环境搭建过程从安装到Hello World
2021/02/05 Python
Mamas & Papas沙特阿拉伯:英国最受欢迎的婴儿品牌
2017/11/20 全球购物
房产公证书范本
2014/04/10 职场文书
护士长竞聘演讲稿
2014/04/30 职场文书
招标承诺书
2014/08/30 职场文书
有限责任公司股东合作协议书
2014/12/02 职场文书
小学思想品德教学反思
2016/02/24 职场文书
导游词之蓬莱长岛
2019/12/17 职场文书
MySQL 5.7常见数据类型
2021/07/15 MySQL
直播实况, OMG破敌三路五十分钟大战神技局摩托车
2022/04/01 DOTA
Python 文字识别
2022/05/11 Python