上一篇文章聊了一下使用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个元素

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

这一步根据自己的业务来定,如果业务规定,这个优惠券就放出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的信息了,这些信息可以由队列消费。

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

PHP+Redis链表解决高并发下商品超卖问题的更多相关文章

  1. PHP+Redis实现高并发下商品超卖问题

    对于一些有一定用户量的电商网站,如果只是单纯的使用关系型数据库(如MySQL.Oracle)来做抢购,对数据库的压力是非常大的,而且如果不使用好数据库的锁机制,还会导致商品.优惠券超卖的问题.我所在的 ...

  2. Redis+Lua解决高并发场景抢购秒杀问题

    之前写了一篇PHP+Redis链表解决高并发下商品超卖问题,今天介绍一些如何使用PHP+Redis+Lua解决高并发下商品超卖问题. 为何要使用Lua脚本解决商品超卖的问题呢? Redis在2.6版本 ...

  3. 使用redis防止抢购商品超卖

    前言: redis不仅仅是单纯的缓存,它还有一些特殊的功能,在一些特殊场景上很好用. 本篇博文用来测试下使用redis来防止抢购商品超卖问题. 内容: 使用redis的list进行测试 思路是设置一个 ...

  4. 以商品超卖为例讲解Redis分布式锁

    本案例主要讲解Redis实现分布式锁的两种实现方式:Jedis实现.Redisson实现.网上关于这方面讲解太多了,Van自认为文笔没他们好,还是用示例代码说明. 一.jedis 实现 该方案只考虑R ...

  5. redis防止抢购商品超卖

    前言: redis不仅仅是单纯的缓存,它还有一些特殊的功能,在一些特殊场景上很好用. 本篇博文用来测试下使用redis来防止抢购商品超卖问题. 内容: 使用redis的list进行测试 思路是设置一个 ...

  6. PHP和Redis实现在高并发下的抢购及秒杀功能示例详解

    抢购.秒杀是平常很常见的场景,面试的时候面试官也经常会问到,比如问你淘宝中的抢购秒杀是怎么实现的等等. 抢购.秒杀实现很简单,但是有些问题需要解决,主要针对两个问题: 一.高并发对数据库产生的压力二. ...

  7. 利用Redis锁解决高并发问题

    这里我们主要利用Redis的setnx的命令来处理高并发. setnx 有两个参数.第一个参数表示键.第二个参数表示值.如果当前键不存在,那么会插入当前键,将第二个参数做为值.返回 1.如果当前键存在 ...

  8. 利用 Redis 锁解决高并发问题

    这里我们主要利用 Redis 的 setnx 的命令来处理高并发. setnx 有两个参数.第一个参数表示键.第二个参数表示值.如果当前键不存在,那么会插入当前键,将第二个参数做为值.返回 1.如果当 ...

  9. 【转】从msql数据库处理高并发商品超卖

    今天王总又给我们上了一课,其实mysql处理高并发,防止库存超卖的问题,在去年的时候,王总已经提过:但是很可惜,即使当时大家都听懂了,但是在现实开发中,还是没这方面的意识.今天就我的一些理解,整理一下 ...

随机推荐

  1. day06 可变不可变类型

    1.可变不可变类型 可变类型 定义:值改变,id不变,改的是原值 不可变类型 定义:值改变,id也变了,证明是产生了新的值没有改变原值 验证 x = 10 print(id(x)) x = 11 pr ...

  2. 纯 CSS 实现滑动轮播图效果

    只使用css实现轮播图简单的操作 <!DOCTYPE html> <html lang="en"> <head> <meta charse ...

  3. Cordova总是弹出Connection to server was Unsuccessful

    开发环境:Cordova版本:6.x 安卓版本:6.0 解决方案一: 在config.xml中添加以下内容: <preference name="LoadUrlTimeoutValue ...

  4. 开源|如何开发一个高性能的redis cluster proxy?

    文|曹佳俊 网易智慧企业资深服务端开发工程师 背    景 redis cluster简介 Redis cluster是redis官方提供集群方案,设计上采用非中心化的架构,节点之间通过gossip协 ...

  5. 对Vue中的MVVM原理解析和实现

    对Vue中的MVVM原理解析和实现 首先你对Vue需要有一定的了解,知道MVVM.这样才能更有助于你顺利的完成下面原理的阅读学习和编写 下面由我阿巴阿巴的详细走一遍Vue中MVVM原理的实现,这篇文章 ...

  6. .NET Core微服务开发网关篇-ocelot

    通过上篇我们知道,网关是外部访问的统一入口,本文采用Ocelot作为Api网关. 环境要求: vs2019 .NetCore3.1 Ocelot16.0.1 创建一个产品服务Api站点(AAStore ...

  7. 用Eclipse进行单元测试JUnit4

    (1)在项目中引入Jar包 (2)编写需要测试的类 public class Calculator {     private static int result=0; // 静态变量,用于存储运行结 ...

  8. tineMCE 踩坑:images_upload_handler

    tineMCE 的官方示例提供了前端上传图片方法 images_upload_handler 的写法. 但官方写的有点问题,上传会报错. 不过修改也很简单: images_upload_handler ...

  9. Jexl表达式引擎-根据字符串动态执行JAVA.md

    Table of Contents generated with DocToc 一.使用场景 二.市面上表达式引擎比较 2.1 Aviator 2.2 Jexl 一.使用场景 在做某些项目的时候,有时 ...

  10. Java中goto标签的使用

    编写此文仅为以后可以复习. 最近在自学Java核心技术(很好的书,推荐!!),也是第一次从上面了解了goto,或许只是浅层了解. 错误之处希望大佬们给予批评与建议!!谢谢!!! Java核心技术中就提 ...