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 相关文章推荐
福利彩票幸运号码自动生成器
Oct 09 PHP
php Undefined index的问题
Jun 01 PHP
使用php+Ajax实现唯一校验实现代码[简单应用]
Nov 29 PHP
PHP页面中文乱码分析
Oct 29 PHP
php calender(日历)二个版本代码示例(解决2038问题)
Dec 24 PHP
php $_SERVER windows系统与linux系统下的区别说明
Feb 14 PHP
thinkphp中空模板与空模块的用法实例
Nov 26 PHP
php绘制一个扇形的方法
Jan 24 PHP
Yii使用技巧大汇总
Dec 29 PHP
redis查看连接数及php模拟并发创建redis连接的方法
Dec 15 PHP
tp5框架无刷新分页实现方法分析
Sep 26 PHP
ThinkPHP5和ThinkPHP6的区别
Mar 31 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
一个比较简单的PHP 分页分组类
2009/12/10 PHP
在PHP中利用wsdl创建标准webservice的实现代码
2011/12/07 PHP
Laravel 连接(Join)示例
2019/10/16 PHP
网页开发中的容易忽略的问题 javascript HTML中的table
2009/04/15 Javascript
JS字符串的切分用法实例
2016/02/22 Javascript
客户端验证用户名和密码的方法详解
2016/06/16 Javascript
Bootstrap CSS布局之图像
2016/12/17 Javascript
Avalonjs双向数据绑定与监听的实例代码
2017/06/23 Javascript
vue项目开发中setTimeout等定时器的管理问题
2018/09/13 Javascript
Layui事件监听的实现(表单和数据表格)
2019/10/17 Javascript
[50:21]Liquid vs Winstrike 2018国际邀请赛小组赛BO2 第二场
2018/08/19 DOTA
[56:56]VG vs LGD 2019国际邀请赛淘汰赛 胜者组 BO3 第一场 8.22
2019/09/05 DOTA
Python获取apk文件URL地址实例
2013/11/01 Python
Python获取电脑硬件信息及状态的实现方法
2014/08/29 Python
用Python从零实现贝叶斯分类器的机器学习的教程
2015/03/31 Python
python 根据正则表达式提取指定的内容实例详解
2016/12/04 Python
python timestamp和datetime之间转换详解
2017/12/11 Python
python 通过 socket 发送文件的实例代码
2018/08/14 Python
Python3中内置类型bytes和str用法及byte和string之间各种编码转换 问题
2018/09/27 Python
为什么str(float)在Python 3中比Python 2返回更多的数字
2018/10/16 Python
使用pandas实现csv/excel sheet互相转换的方法
2018/12/10 Python
Python功能点实现:函数级/代码块级计时器
2019/01/02 Python
Python猴子补丁知识点总结
2020/01/05 Python
利用CSS3的transform做的动态时钟效果
2011/09/21 HTML / CSS
法国包包和行李箱销售网站:Bagage24.fr
2020/03/24 全球购物
高职教师岗位职责
2013/12/24 职场文书
教师试用期自我鉴定
2014/02/12 职场文书
化妆品促销方案
2014/02/24 职场文书
工程质量承诺书范文
2014/03/27 职场文书
小学感恩节活动总结
2015/03/24 职场文书
借款民事起诉状范文
2015/05/19 职场文书
写作之关于描写老人的好段摘抄
2019/11/14 职场文书
教你怎么用python爬取爱奇艺热门电影
2021/05/20 Python
JS数组方法some、every和find的使用详情
2021/10/05 Javascript
B站评分公认最好看的动漫,你的名字评分9.9,第六备受喜欢
2022/03/18 日漫
Python用tkinter实现自定义记事本的方法详解
2022/03/31 Python