问题:

抢购/秒杀是如今很常见的一个应用场景,那么高并发竞争下如何解决超抢(或超卖库存不足为负数的问题)呢?

常规写法:

查询出对应商品的库存,看是否大于0,然后执行生成订单等操作,但是在判断库存是否大于0处,如果在高并发下就会有问题,导致库存量出现负数

分析 & 方案

这里我就只谈redis的解决方案吧...

我们先来看以下代码(这里我以laravel为例吧)是否能正确解决超抢/卖的问题:

$num = 10; //系统库存量

$user_id = \Session::get('user_id');//当前抢购用户id

$len = \Redis::llen('order:1'); //检查库存,order:1 定义为健名

if($len >= $num)

return '已经抢光了哦';

$result = \Redis::lpush('order:1',$user_id); //把抢到的用户存入到列表中

if($result)

return '恭喜您!抢到了哦';

如果代码正常运行,按照预期理解的是列表order:1中最多只能存储10个用户的id,因为库存只有10个。 然而,但是,在使用jmeter工具模拟多用户并发请求时,最后发现order:1中总是超过5个用户,也就是出现了“超抢/超卖”。

分析问题就出在这一段代码:

$len = \Redis::llen('order:1'); //检查库存,order:1 定义为健名

if($len >= $num)

return '已经抢光了哦';

在抢购进行到一定程度,假如现在已经有9个人抢购成功,又来了3个用户同时抢购,这时if条件将会被绕过(条件同时被满足了),这三个用户都能抢购成功。而实际上只剩下一件库存可以抢了。

在高并发下,很多看似不大可能是问题的,都成了实际产生的问题了。**要解决“超抢/超卖”的问题,核心在于保证检查库存时的操作是依次执行的,再形象的说就是把“多线程”转成“单线程”。**即使有很多用户同时到达,也是一个个检查并给与抢购资格,一旦库存抢尽,后面的用户就无法继续了。

我们需要使用redis的原子操作来实现这个“单线程”。首先我们把库存存在goods_store:1这个列表中,假设有10件库存,就往列表中push10个数,这个数没有实际意义,仅仅只是代表一件库存。抢购开始后,每到来一个用户,就从goods_store:1中pop一个数,表示用户抢购成功。当列表为空时,表示已经被抢光了。因为列表的pop操作是原子的,即使有很多用户同时到达,也是依次执行的。抢购的示例代码如下:

比如这里我先把库存(可用库存,这里我强调下哈,一般都是商品详情页抢购,后来者进来看到的库存可能不再是后台系统配置的10个库存数了)放入redis队列:

在抢购进行到一定程度,假如现在已经有9个人抢购成功,又来了3个用户同时抢购,这时if条件将会被绕过(条件同时被满足了),这三个用户都能抢购成功。而实际上只剩下一件库存可以抢了。

在高并发下,很多看似不大可能是问题的,都成了实际产生的问题了。要解决“超抢/超卖”的问题,核心在于保证检查库存时的操作是依次执行的,再形象的说就是把“多线程”转成“单线程”。即使有很多用户同时到达,也是一个个检查并给与抢购资格,一旦库存抢尽,后面的用户就无法继续了。

我们需要使用redis的原子操作来实现这个“单线程”。首先我们把库存存在goods_store:1这个列表中,假设有10件库存,就往列表中push10个数,这个数没有实际意义,仅仅只是代表一件库存。抢购开始后,每到来一个用户,就从goods_store:1中pop一个数,表示用户抢购成功。当列表为空时,表示已经被抢光了。因为列表的pop操作是原子的,即使有很多用户同时到达,也是依次执行的。抢购的示例代码如下:

比如这里我先把库存(可用库存,这里我强调下哈,一般都是商品详情页抢购,后来者进来看到的库存可能不再是后台系统配置的10个库存数了)放入redis队列:

好吧,抢购时间到了:

/* 模拟抢购操作,抢购前判断redis队列库存量 */

$count=\Redis::lpop('goods_store:1');//lpop是移除并返回列表的第一个元素。

if(!$count)

return '已经抢光了哦';

/* 下面处理抢购成功流程 */

\DB::table('goods')->decrement('num', 1);//减少num库存字段

用户抢购成功后,上面的我们也可以稍微优化下,比如我们可用将用户ID存入了order:1列表中。接下来我们可以引导这些用户去完成订单的其他步骤,到这里才涉及到与数据库的交互。最终只有很少的人走到这一步吧,也就解决的数据库的压力问题。

我们再改下上面的代码:

$user_id = \Session::get('user_id');//当前抢购用户id

/* 模拟抢购操作,抢购前判断redis队列库存量 */

$count=\Redis::lpop('goods_store:1');

if(!$count)

return '已经抢光了哦';

$result = \Redis::lpush('order:1',$user_id);

if($result)

return '恭喜您!抢到了哦';

为了检测实际效果,我使用jmeter工具模拟100、200、1000个用户并发进行抢购,经过大量的测试,最终抢购成功的用户始终为10,没有出现“超抢/超卖”。

上面只是简单模拟高并发下的抢购思路,真实场景要比这复杂很多,比如双11活动远远比这更复杂多啦,很多注意的地方如抢购活动页面做成静态的,通过ajax调用接口

再如上面的会导致一个用户抢多个,思路:

需要一个排队队列(比如:queue:1,以user_id为值的列表)和抢购结果队列(比如:order:1,以user_id为值的列表)及库存队列(比如上面的goods_store:1)。高并发情况,先将用户进入排队队列,用一个线程循环处理从排队队列取出一个用户,判断用户是否已在抢购结果队列,如果在则已抢购,否则未抢购,接着执行库存减1,写入数据库,将此user_id用户同时也进入结果队列。

java redis实现抢购_【抢购/秒杀】redis实现高并发下的抢购/秒杀功能相关推荐

  1. PHP redis秒杀返回结果,php结合redis实现高并发下的抢购、秒杀功能

    抢购.秒杀是如今很常见的一个应用场景,主要需要解决的问题有两个: 1 高并发对数据库产生的压力 2 竞争状态下如何解决库存的正确减少("超卖"问题) 对于第一个问题,已经很容易想到 ...

  2. php结合redis实现高并发下的抢购、秒杀功能

    抢购.秒杀是如今很常见的一个应用场景,主要需要解决的问题有两个: 1 高并发对数据库产生的压力 2 竞争状态下如何解决库存的正确减少("超卖"问题) 对于第一个问题,已经很容易想到 ...

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

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

  4. 抢购活动php,php结合redis实现高并发下的抢购、秒杀功能

    使用redis队列,因为pop操作是原子的,即使有很多用户同时到达,也是依次执行,推荐使用(mysql事务在高并发下性能下降很厉害,文件锁的方式也是)先将商品库存如队列<?php $store= ...

  5. 简单实现redis实现高并发下的抢购/秒杀功能

    简述 抢购/秒杀是如今很常见的一个应用场景,那么高并发竞争下如何解决超抢(或超卖库存不足为负数的问题)呢? 常规写法: 查询出对应商品的库存,看是否大于0,然后执行生成订单等操作,但是在判断库存是否大 ...

  6. redis是什么_什么是Redis?为什么我们要用Redis?

    前言 当结束Java和数据库的学习以后,你就会接触到Redis这个词,我第一次听到的时候脑海里就会浮现这两个问题: 什么是Redis?为什么我们要用Redis? 我了解完以后,写出来帮助大家能够更快的 ...

  7. 安装redis并开启_如何安装Redis,以及对Redis配置文件的更改和测试

    Redis的安装 1.下载redis包文件https://redis.io/ 2. 上传安装包(将其压缩包拖入Linux中所需上传的目录下) 3.解压安装包 tar -zxvf redis-5.0.5 ...

  8. netcore redis 存储集合_.net core redis的全套操作

    摘要: 发布订阅 2.在.netcore中的调用订阅:redisManager.Subscribe("dylan",(channel,value)=>{Console.Wri ...

  9. redis stream持久化_[灌水] Redis 的持久化

    关于 Redis 的 Persistence, 最好的入门材料应该是: 它有两种形式,AOF 和 RDB:RDB 相对来说是一个数据库的"snapshot",通过 SAVE 或者 ...

最新文章

  1. NLP 解决方案是如何被深度学习改写的?
  2. SpringBoot使用教程【1】Restful API设计 返回json,xml格式...
  3. 自建通用Makefile 分享
  4. centos 6.5 配置LDAP服务器+客户端!
  5. Python中的除法 整除 非整除
  6. php+mssql 分页,一个PHP+MSSQL分页的例子
  7. leetcode307. Range Sum Query - Mutable
  8. 机器学习实战-聚类分析KMEANS算法-25
  9. Django的自带认证系统——auth模块
  10. MaintainableCSS 《可维护性 CSS》 --- 模板篇
  11. Ubuntu中安装Eclipse的SVN插件——subclipse
  12. ​什么问题最让程序员头秃?我们分析了11种语言的11000个问题
  13. 【读书笔记】摘自Google开源项目风格指南-C++风格指南
  14. 【动态规划】最大子段和问题,最大子矩阵和问题,最大m子段和问题
  15. javasript |仿百度换肤效果
  16. python: 上下文管理器(context manager)
  17. 静态代理和动态代理的区别,什么场景使用?
  18. 【pytorch基础学习笔记】零基础速成,了解pytorch基础语法和应用
  19. ts视频合并---P站
  20. Excel 6位数字与MD5对照表 100000-999999

热门文章

  1. dpdk-20.11 编译和安装
  2. 某985学历程序员嫌弃女朋友高职毕业学历低,但女朋友实在太漂亮,好犹豫!...
  3. 安全协议系列(一)----WEP详解
  4. css清除浮动的几种方式
  5. 下班时间学写作练习17天
  6. 基于自适应形态学的探月雷达噪声压制方法
  7. tl-wdr7300虚拟服务器怎么设置,TP-Link TL-WDR7300路由器无线桥接怎么设置?
  8. 你真的适合学习JAVA开发吗?
  9. 计算机一级ms必考知识点,计算机一级MsOffice考试知识点整理重点.docx
  10. 播音计算机论文,播音主持论文的题目或论文内容