PHP+Redis链表解决高并发下商品超卖问题(实现原理及步骤)


Posted in PHP onAugust 03, 2020

上一篇文章聊了一下使用Redis事务来解决高并发商品超卖问题,今天我们来聊一下使用Redis链表来解决高并发商品超卖问题。

实现原理

使用redis链表来做,因为pop操作是原子的,即使有很多用户同时到达,也是依次执行,推荐使用。

实现步骤

第一步,先将商品库存入队列

/**
 * 添加商品数量到商品队列
 * @param int $couponId 优惠券ID
 */
function addCoupons($couponId)
{
 //1.初始化Redis连接
 $redis = new Redis();
 if (!$redis->connect('127.0.0.1', 6379)) {
 trigger_error('Redis连接出错!!!', E_USER_ERROR);
 } else {
 echo '连接正常<br>';
 }

 //根据优惠券ID从数据库中查询该优惠券的库存量
 //$sql = "select id, stock from coupon where id = {$couponId}";
 $stock = 10; //假设10就是我们从数据库中查询出的该优惠券在数据库中的库存量

 //我们现在将这10个库存放入到以该商品ID为key的redis链表中,有几件库存,就存入多少次1,链表长度代表商品库存数
 for($i = 0; $i < $stock; $i++) {
 $redis->lPush("secKill:".$couponId.":stock", 1);
 }

 $redis->close();
}
$couponId = 11211;
addCoupons($couponId);

我们调用该方法,然后查看redis,链表中已经添加了10个元素

PHP+Redis链表解决高并发下商品超卖问题(实现原理及步骤)

第二步,抢购开始,设置库存的缓存周期

这一步根据自己的业务来定,如果业务规定,这个优惠券就放出2分钟给用户抢,那么就通过expire()方法给链表设置一个有效期,即使是在有效期内没有抢完仍然有库存也不让用户抢了(由于我们公司业务不对优惠券抢券设置有效期,所以这一步我不需要做)

//设置链表有效期是两分钟
$redis->expire('key', 120);

第三步,客户端执行瞬时抢购操作

/**
 * 抢优惠券(秒杀)
 * @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>';
 }

 //将已经成功抢购的用户添加到该以该商品ID为key的集合(set)中
 //如果用户已经在集合中,说明用户已经成功秒杀过一次了,不允许再次参与秒杀
 if ($redis->sIsMember('secKill:'.$couponId.':uid', $uid)) {
 echo '秒杀失败';
 return false;
 }

 //秒杀商品的库存key
 $key = 'secKill:'.$couponId.':stock';

 //从以该优惠券ID为key的链表中弹出一个值,如果有值,证明优惠券还有库存
 $isSockNotEmpty = $redis->lPop($key);

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

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

 //将成功抢券的用户记录到集合中,防止被已抢过的用户再次秒杀
 $redis->sAdd('secKill:'.$couponId.':uid', $uid);
 $redis->close();
 return true;
}

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

第四步,将成功秒杀的用户入数据库持久化数据,对于并发量不是很大的抢购,我们可以在第三步成功抢购后直接将信息写入数据库,对于并发量比较大的可以放入RabbitMQ消息队列中消费(推荐使用RabbitMQ队列而不是redis是因为RabbitMQ可以保证消息百分之百的被消费,而redis就相对没有那么稳定与可靠)

//此处代码省略
//根据自己的业务场景看看是入数据库还是放入rabbitMQ消息队列中消费

现在我们使用ab工具模拟高并发下的抢券行为(2000次请求数,100并发量)

ab -n 2000 -c 100 www.test.com/

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

同样的,couponOrder队列里已经有了10份包含用户uid和优惠券id的信息了,这些信息可以由队列消费。

PHP+Redis链表解决高并发下商品超卖问题(实现原理及步骤)

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

PHP+Redis链表解决高并发下商品超卖问题(实现原理及步骤)

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

PHP 相关文章推荐
用PHP 4.2书写安全的脚本
Oct 09 PHP
BBS(php &amp; mysql)完整版(四)
Oct 09 PHP
Windows IIS PHP 5.2 安装与配置方法
Jun 08 PHP
dedecms 批量提取第一张图片最为缩略图的代码(文章+软件)
Oct 29 PHP
屏蔽机器人从你的网站搜取email地址的php代码
Nov 14 PHP
使用php实现快钱支付功能(涉及到接口)
Jul 01 PHP
destoon调用企业会员公司形象图片的实现方法
Aug 21 PHP
php中字符集转换iconv函数使用总结
Oct 11 PHP
php实现网站顶踩功能的完整前端代码
Jul 19 PHP
Yii rules常用规则示例
Mar 15 PHP
PHP中soap用法示例【SoapServer服务端与SoapClient客户端编写】
Dec 25 PHP
原生PHP实现导出csv格式Excel文件的方法示例【附源码下载】
Mar 07 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
phpStudy vscode 搭建debug调试的教程详解
Jul 28 #PHP
You might like
后宫无数却洁身自好的男主,唐三只爱小舞
2020/03/02 国漫
PHP用mysql数据库存储session的代码
2010/03/05 PHP
PHP setcookie设置Cookie用法(及设置无效的问题)
2011/07/13 PHP
解析PHP获取当前网址及域名的实现代码
2013/06/23 PHP
mantis安装、配置和使用中的问题小结
2014/07/14 PHP
详解php比较操作符的安全问题
2015/12/03 PHP
php安装php_rar扩展实现rar文件读取和解压的方法
2016/11/17 PHP
Array.prototype.slice.apply的使用方法
2010/03/17 Javascript
在多个页面使用同一个HTML片段《续》
2011/03/04 Javascript
整理一些JavaScript的IE和火狐的兼容性注意事项
2011/03/17 Javascript
当前页禁止复制粘贴截屏代码小集
2013/07/24 Javascript
用Jquery.load载入页面后样式没了页面混乱的解决方法
2014/10/20 Javascript
详解JavaScript基于面向对象之继承
2015/12/13 Javascript
JS组件系列之Bootstrap table表格组件神器【终结篇】
2016/05/10 Javascript
JavaScript中return用法示例
2016/11/29 Javascript
jQuery插件JWPlayer视频播放器用法实例分析
2017/01/11 Javascript
Easyui Datagrid自定义按钮列(最后面的操作列)
2017/07/13 Javascript
node.js express中app.param的用法详解
2017/07/16 Javascript
vue实现图片加载完成前的loading组件方法
2018/02/05 Javascript
React router动态加载组件之适配器模式的应用详解
2018/09/12 Javascript
jQuery 常用特效实例小结【显示与隐藏、淡入淡出、滑动、动画等】
2020/05/19 jQuery
如何用JS模拟实现数组的map方法
2020/07/30 Javascript
[00:12]2018DOTA2亚洲邀请赛 Somnus丶M出阵单挑
2018/04/06 DOTA
pycharm设置当前工作目录的操作(working directory)
2020/02/14 Python
django haystack实现全文检索的示例代码
2020/06/24 Python
在 Python 中使用 MQTT的方法
2020/08/18 Python
详解python的super()的作用和原理
2020/10/29 Python
2014年公司庆元旦活动方案
2014/03/05 职场文书
2014年巴西世界杯口号
2014/06/05 职场文书
创先争优公开承诺书
2014/08/30 职场文书
连锁超市项目计划书
2014/09/15 职场文书
学生抄作业检讨书(2篇)
2014/10/17 职场文书
2014年营销工作总结
2014/11/22 职场文书
出纳工作检讨书范文
2014/12/27 职场文书
电力安全学习心得体会
2016/01/18 职场文书
2019年亲子运动会口号
2019/10/11 职场文书