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 开发环境配置(测试开发环境)
Apr 28 PHP
php 验证码实例代码
Jun 01 PHP
php设计模式 Composite (组合模式)
Jun 26 PHP
跟我学Laravel之安装Laravel
Oct 15 PHP
php字符串截取函数用法分析
Nov 25 PHP
php生成PDF格式文件并且加密
Jun 22 PHP
PHP数据库操作Helper类完整实例
May 11 PHP
PHP使用自定义方法实现数组合并示例
Jul 07 PHP
PHP简单检测网址是否能够正常打开的方法
Sep 04 PHP
PHP版微信第三方实现一键登录及获取用户信息的方法
Oct 14 PHP
Zend Framework路由器用法实例详解
Dec 11 PHP
PHP+Mysql+Ajax实现淘宝客服或阿里旺旺聊天功能(前台页面)
Jun 16 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实现像JSP,ASP里Application那样的全局变量
2007/01/12 PHP
PHP执行linux系统命令的常用函数使用说明
2010/04/27 PHP
深入PHP与浏览器缓存的分析
2013/06/03 PHP
解析php addslashes()与addclashes()函数的区别和比较
2013/06/24 PHP
destoon安全设置中需要设置可写权限的目录及文件
2014/06/21 PHP
js内置对象 学习笔记
2011/08/01 Javascript
js判断登录与否并确定跳转页面的方法
2015/01/30 Javascript
JavaScript中for循环的使用详解
2015/06/03 Javascript
基于Jquery实现仿百度百科右侧导航代码附源码下载
2015/11/27 Javascript
基于JavaScript实现图片点击弹出窗口而不是保存
2016/02/06 Javascript
有关easyui-layout中的收缩层无法显示标题的解决办法
2016/05/10 Javascript
js 能实现监听F5页面刷新子iframe 而父页面不刷新的方法
2016/11/09 Javascript
JavaScript之iterable_动力节点Java学院整理
2017/06/29 Javascript
理解 javascript 中的函数表达式与函数声明
2017/07/07 Javascript
解决vue-cli项目webpack打包后iconfont文件路径的问题
2018/09/01 Javascript
vue中如何实现后台管理系统的权限控制的方法步骤
2019/09/05 Javascript
vue  elementUI 表单嵌套验证的实例代码
2019/11/06 Javascript
如何修改Vue打包后文件的接口地址配置的方法
2020/04/22 Javascript
Python实现全角半角字符互转的方法
2016/11/28 Python
Python实现采用进度条实时显示处理进度的方法
2017/12/19 Python
python3下实现搜狗AI API的代码示例
2018/04/10 Python
python sort、sort_index方法代码实例
2019/03/28 Python
python生成requirements.txt的两种方法
2019/09/18 Python
python实现复制文件到指定目录
2019/10/16 Python
pandas实现DataFrame显示最大行列,不省略显示实例
2019/12/26 Python
使用Keras构造简单的CNN网络实例
2020/06/29 Python
基于python实现操作redis及消息队列
2020/08/27 Python
Python 列表推导式需要注意的地方
2020/10/23 Python
html5 Canvas画图教程(2)—画直线与设置线条的样式如颜色/端点/交汇点
2013/01/09 HTML / CSS
德国咖啡批发商:Coffeefair
2019/08/26 全球购物
2014年向国旗敬礼活动方案
2014/09/27 职场文书
清明节扫墓活动总结
2015/02/09 职场文书
个人简历自我评价怎么写
2015/03/10 职场文书
退伍军人感言
2015/08/01 职场文书
离婚协议书范本(2016最新版)
2016/03/18 职场文书
中秋节作文(五年级)之关于月亮
2019/09/11 职场文书