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 中执行系统外部命令
Oct 09 PHP
PHP中动态显示签名和ip原理
Mar 28 PHP
php仿discuz分页效果代码
Oct 02 PHP
用php的ob_start来生成静态页面的方法分析
Mar 09 PHP
MySQL连接数超过限制的解决方法
Jul 17 PHP
Zend的MVC机制使用分析(一)
May 02 PHP
php过滤所有恶意字符(批量过滤post,get敏感数据)
Mar 18 PHP
PHP利用正则表达式将相对路径转成绝对路径的方法示例
Feb 28 PHP
几行代码轻松实现PHP文件打包下载zip
Mar 01 PHP
yii框架redis结合php实现秒杀效果(实例代码)
Oct 26 PHP
ThinkPHP3.2框架自定义配置和加载用法示例
Jun 14 PHP
php-fpm超时时间设置request_terminate_timeout资源问题分析
Sep 27 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
如何将数据从文本导入到mysql
2006/10/09 PHP
使用Limit参数优化MySQL查询的方法
2008/11/12 PHP
php模板中出现空行解决方法
2011/03/08 PHP
基于MySQL分区性能的详细介绍
2013/05/02 PHP
深入密码加salt原理的分析
2013/06/06 PHP
深入for,while,foreach遍历时间比较的详解
2013/06/08 PHP
PHP文件操作方法汇总
2015/07/01 PHP
PHP指定截取字符串中的中英文或数字字符的实例分享
2016/03/18 PHP
php 5.4 全新的代码复用Trait详解
2017/01/05 PHP
php设计模式之抽象工厂模式分析【星际争霸游戏案例】
2020/01/23 PHP
js精美的幻灯片画集特效代码分享
2015/08/29 Javascript
javascript实现Email邮件显示与删除功能
2015/11/21 Javascript
实例解析jQuery中如何取消后续执行内容
2016/12/01 Javascript
React组件生命周期详解
2017/07/03 Javascript
jQuery选择器之子元素过滤选择器
2017/09/28 jQuery
webpack中CommonsChunkPlugin详细教程(小结)
2017/11/09 Javascript
javaScript强制保留两位小数的输入数校验和小数保留问题
2018/05/09 Javascript
深入理解python中的select模块
2017/04/23 Python
numpy使用fromstring创建矩阵的实例
2018/06/15 Python
基于django channel实现websocket的聊天室的方法示例
2019/04/11 Python
通过celery异步处理一个查询任务的完整代码
2019/11/19 Python
Django多层嵌套ManyToMany字段ORM操作详解
2020/05/19 Python
Python实现计算图像RGB均值方式
2020/06/04 Python
一款基于css3麻将筛子3D翻转特效的实例教程
2014/12/31 HTML / CSS
html5应用缓存_动力节点Java学院整理
2017/07/13 HTML / CSS
详解H5本地储存Web Storage
2017/07/03 HTML / CSS
美国高级工作服品牌:Carhartt
2018/01/25 全球购物
Petmate品牌官方网站:宠物用品
2018/11/25 全球购物
ECCO英国官网:丹麦鞋履品牌
2019/09/03 全球购物
Timberland德国官网:靴子、鞋子、衣服、夹克及配件
2019/12/10 全球购物
企业安全标语
2014/06/07 职场文书
乡村教师党员四风问题对照检查材料思想汇报
2014/10/08 职场文书
2014年客服工作总结范文
2014/11/13 职场文书
2015年政府采购工作总结
2015/05/21 职场文书
JavaScript中MutationObServer监听DOM元素详情
2021/11/27 Javascript
《艾尔登法环》1.03.3补丁上线 碎星伤害调整
2022/04/07 其他游戏