PHP+Redis实现高并发下商品超卖问题
对于一些有一定用户量的电商网站,如果只是单纯的使用关系型数据库(如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;
}
//代码走到这里,说明该用户是第一次参与秒杀,将库存减一,然后把这个人放到已抢到的集合表
$redisMulti = $redis->multi();
$redisMulti->decr($key);
$redisMulti->sAdd('secKill:'.$couponId.':uid', $uid);
$result = $redisMulti->exec();
if (empty($result)) {//事务被取消
echo '秒杀失败';
return false;
}
//抢券成功,将优惠券ID和UID放入到队列中,由一个单独的进程队列来消费队列里的数据,向用户推送抢到的优惠券
$redis->lPush('couponOrder', $couponId.'+'.$uid);
return true;
}
$couponId = 11211;
$uid = mt_rand(1, 100);
secKill($couponId, $uid);
首先,我模拟设置优惠券ID为11211的优惠券库存为10个。

然后,我们使用ab工具来模拟1000次请求,50并发量来测试
ab -n 1000 -c 50 www.test.com/
然后我们通过Redis Desktop Manager来查看一些Redis的结果
couponOrder队列里已经有了10个用户的信息了

并且优惠券的剩余数量也是0了,不再是负数了

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

上面这串代码解决了两个问题:
- 解决了瞬时的大量查询到数据库上给数据库造成很大压力的问题,流量都被拦截在了redis缓存层
- 解决了优惠券被超库存抢购的问题
但是,这段代码也存在一定的问题:
没有使用redis连接池,频繁创建新的redis有一定的性能影响
由于使用了事务,每一次并发请求中只会有一个用户抢券成功,该并发请求中的其它用户都会失败,只能等第二次并发
同样还是事务导致的库存遗留问题,如果有10个商品,1000次请求每次200并发量,5次并发请求就完成了1000次请求,但是只会有5个用户成功抢到,如果没有后续的请求,会导致库存还有5份存量
提示:在消费队列里,如果优惠券发放失败,一定要立即记录并短信通知运营管理人员,看看是否能重发或者通过后台手动定向推送给用户。
所以,后续我又使用了lua脚本和redis配合一起来解决了这个问题。具体代码,我会后续整理处理补充完整。
转载请注明连接:https://www.cnblogs.com/itbsl/p/13418176.html
PHP+Redis实现高并发下商品超卖问题的更多相关文章
- PHP+Redis链表解决高并发下商品超卖问题
目录 实现原理 实现步骤 上一篇文章聊了一下使用Redis事务来解决高并发商品超卖问题,今天我们来聊一下使用Redis链表来解决高并发商品超卖问题. 实现原理 使用redis链表来做,因为pop操作是 ...
- php结合Redis实现高并发下的秒杀抢购功能
实现思路 准备两个队列A和B,假设A队列的名称为stock,用于存放商品总库存信息,B队列的名称为users,用于存放抢购成功后的用户信息.每当有用户进行抢购操作时,先从A队列弹出一个元素,如果该元素 ...
- 使用redis防止抢购商品超卖
前言: redis不仅仅是单纯的缓存,它还有一些特殊的功能,在一些特殊场景上很好用. 本篇博文用来测试下使用redis来防止抢购商品超卖问题. 内容: 使用redis的list进行测试 思路是设置一个 ...
- 以商品超卖为例讲解Redis分布式锁
本案例主要讲解Redis实现分布式锁的两种实现方式:Jedis实现.Redisson实现.网上关于这方面讲解太多了,Van自认为文笔没他们好,还是用示例代码说明. 一.jedis 实现 该方案只考虑R ...
- redis防止抢购商品超卖
前言: redis不仅仅是单纯的缓存,它还有一些特殊的功能,在一些特殊场景上很好用. 本篇博文用来测试下使用redis来防止抢购商品超卖问题. 内容: 使用redis的list进行测试 思路是设置一个 ...
- php结合redis实现高并发下的抢购、秒杀功能
抢购.秒杀是如今很常见的一个应用场景,主要需要解决的问题有两个:1 高并发对数据库产生的压力2 竞争状态下如何解决库存的正确减少("超卖"问题)对于第一个问题,已经很容易想到用缓存 ...
- (高级篇)php结合redis实现高并发下的抢购、秒杀功能
抢购.秒杀是如今很常见的一个应用场景,主要需要解决的问题有两个:1 高并发对数据库产生的压力2 竞争状态下如何解决库存的正确减少("超卖"问题)对于第一个问题,已经很容易想到用缓存 ...
- redis实现高并发下的抢购/秒杀功能
之前写过一篇文章,高并发的解决思路(点此进入查看),今天再次抽空整理下实际场景中的具体代码逻辑实现吧:抢购/秒杀是如今很常见的一个应用场景,那么高并发竞争下如何解决超抢(或超卖库存不足为负数的问题)呢 ...
- 【转】php结合redis实现高并发下的抢购、秒杀功能
抢购.秒杀是如今很常见的一个应用场景,主要需要解决的问题有两个:1 高并发对数据库产生的压力2 竞争状态下如何解决库存的正确减少("超卖"问题)对于第一个问题,已经很容易想到用缓存 ...
随机推荐
- jmeter设置HTTP代理,录制APP脚本
1.打开jmeter,“工作台”右键——“添加”——“非测试元件”——“HTTP代理服务器” 2.设置端口号,手机需与这里的端口号一致 3.新建线程组,“测试计划”右键——“添加”——“Threads ...
- 2020年Java基础高频面试题汇总(1.4W字详细解析,你能遇到的都在这了)
1. Java语言有哪些特点 (1)简单易学.有丰富的类库 (2)面向对象(Java最重要的特性,让程序耦合度更低,内聚性更高) (3)与平台无关性(JVM是Java跨平台使用的根本) (4)可靠安全 ...
- JVM(完成度95%,不断更新)
一.HotSpot HotSpot是最新的虚拟机,替代了JIT,提高Java的运行性能.Java原先是将源代码编译为字节码在虚拟机运行,HotSpot将常用的部分代码编译为本地代码. 对象创建过程 类 ...
- 查看sudo的history:配置sudolog
sudo 权力很大,但责任更重大! We trust you have received the usual lecture from the local System Administrator. ...
- 【初学】Spring源码笔记之零:阅读源码
笔记要求 了解Java语言 了解Spring Framework的基础 会使用Maven 关于本笔记 起因 本职数据分析,为公司内部人员开发数据处理系统,使用了Python/Django+Bootst ...
- MySQL和PHP中以整型存储IP地址
正文:将IP地址以整型存储 一般我们在数据库中会用到ip地址用来查记录的等等,而ip地址是分为四段的,一般是用varchar或char类型存储.但是其实有更好的存储方法就是以整型存储IP地址. 因为c ...
- 如何让touchmove之后不触发touchend的事件
手机扫码看效果 不多说,直接上代码 <ul id="Ul"> <li>111</li> <li>222</li> < ...
- python 装饰器(七):装饰器实例(四)类装饰器装饰类以及类方法
类装饰器装饰类方法 不带参数 from functools import wraps import types class CatchException: def __init__(self,orig ...
- LDAP脚本批量导出用户
背景:工作原因,搭建了LDAP服务,然后用户数过多,因为懒所以就通过python代码生成ldap脚本进行批量导入用户 1.整理用户名单,格式如下: 注:上述格式影响代码中的excel读取代码 2.py ...
- MVC + EFCore 项目实战 - 数仓管理系统4 – 需求分解
上次课程我们完成了项目基本的UI风格配置. 现在就开始进入我们的需求开发,我们先捋一下需求. 一.总体需求说明 项目背景第一篇文章已有介绍,我们回顾一下. 这是一个数据管理"工具类" ...