使用Redis实现抢购的一种思路(list队列实现)
原文:https://my.oschina.net/chinaxy/blog/1829233
抢购是如今很常见的一个应用场景,主要需要解决的问题有两个:
1 高并发对数据库产生的压力
2 竞争状态下如何解决库存的正确减少(“超卖”问题)
对于第一个问题,已经很容易想到用缓存来处理抢购,避免直接操作数据库,例如使用Redis。重点在于第二个问题,我们看看下面一种常规的实现代码:
<?php
require('predis/src/Autoloader.php');
$redis = new Predis\Client(array(
'scheme' => 'tcp',
'host' => '127.0.0.1',
'port' => '6379'
));
//redis 登录
$redis->auth('123456');
//库存
$num = 10;
//用户id
$user_id = $_SESSION['user_id'];
//检查库存
$len = $redis->llen('order:1');
if($len >= $num){
exit('已经抢光了');
}
//把抢到的用户存入到列表中
$result = $redis->lpush('order:1',$user_id);
if($result){
echo '抢到了';
}
?>
如果代码正常运行,列表order:1中最多只能存储10个用户的id,因为库存只有10个。
然而,在使用Apache AB工具模拟很多用户并发请求时,最后发现order:1中总是超过10个用户,也就是出现了“超卖”。
问题就出在这一段代码:
//检查库存
$len = $redis->llen('order:1');
if($len >= $num){
exit('已经抢光了');
}
在抢购进行到一定程度,假如现在已经有9个人抢购成功,又来了3个用户同时抢购,这时if条件将会被绕过,这三个用户都能抢购成功。而实际上只有一件库存可以抢了。
在高并发下,很多不是问题的,都成了问题。要解决“超卖”问题,核心在于保证检查库存时的操作是依次执行的,形象的说就是把“多线程”转成“单线程”。即使有很多用户同时到达,也是一个个检查并给与抢购资格,一旦库存抢尽,后面的用户就无法继续了。
我们需要使用Redis的原子操作来实现这个“单线程”。首先我们把库存存在goods:1这个列表中,假设有10件库存,就往列表中push10个数,这个数没有实际意义,仅仅代表一件库存。抢购开始后,每到来一个用户,就从goods:1中pop一个数,表示用户抢购成功。当列表为空时,表示已经被抢光了。因为列表的pop操作是原子的,即使有很多用户同时到达,也是依次执行的。抢购的示例代码如下:
<?php
//抢购
require('predis/src/Autoloader.php');
$redis = new Predis\Client(array(
'scheme' => 'tcp',
'host' => '127.0.0.1',
'port' => '6379'
));
$redis->auth('123456');
//用户ID
$user_id = $_SESSION['user_id'];
$check = $redis->lpop('goods:1');
if(!$check){
exit('抢光了');
}
$result = $redis->lpush('order:1',$user_id);
if($result){
echo '抢购成功';
}
?>
用户抢购成功后,我们将用户ID存入了order:1列表中。接下来我们可以引导这些用户去完成订单的其他步骤,这里才涉及到与数据库的交互。最终只有很少的人走到这一步,也就解决的数据库的压力问题。
为了检测实际效果,我使用Apache AB工具模拟10、20、1000个用户并发进行抢购,经过大量的测试,最终抢购成功的用户始终为10,没有出现“超卖”。
使用Redis实现抢购的一种思路(list队列实现)的更多相关文章
- [Redis]处理定时任务的2种思路
用redis完成类似 at 命令的功能,例如订单24小时后没有支付自动关闭,定时发邮件,主要说下任务生成之后怎么触发消费. 使用 有序集合 思路: 使用sorted Sets的自动排序, key 为任 ...
- redis缓存击穿问题一种思路分享
思路每一个key都有一个附属key1,附属key1可以是key加特定前缀组成,key对应value为真正的缓存数据,附属key1对应的value不重要,可以是随便一个值,附属key1的作用主要是维护缓 ...
- js数组去重几种思路
在一些后台语言中都内置了一些方法来处理数组或集合中重复的数据.但是js中并没有类似的方法,网上已经有一些方法,但是不够详细.部分代码来源于网络.个人总计如下:大致有4种思路 1)使用两次循环比较原始的 ...
- CSS实现水平垂直同时居中的5种思路
× 目录 [1]水平对齐+行高 [2]水平+垂直对齐 [3]margin+垂直对齐[4]absolute[5]flex 前面的话 水平居中和垂直居中已经单独介绍过,本文将介绍水平垂直同时居中的5种思路 ...
- CSS实现垂直居中的4种思路
× 目录 [1]line-height [2]vertical-align [3]absolute [4]flex 前面的话 相对于水平居中,人们对于垂直居中略显为难,大部分原因是vertical-a ...
- CSS实现水平居中的4种思路
× 目录 [1]text-align [2]margin [3]absolute [4]flex 前面的话 水平居中是经常遇到的问题.看似方法较多,条条大路通罗马.但系统梳理下,其实都围绕着几个思路展 ...
- 应对Memcached缓存失效,导致高并发查询DB的四种思路(l转)
当Memcached缓存失效时,容易出现高并发的查询DB,导致DB压力骤然上升. 这篇blog主要是探讨如何在缓存将要失效时,及时地更新缓存,而不是如何在缓存失效之后,如何防止高并发的DB查询. 解决 ...
- 点击页面div弹窗以外隐藏的两种思路
在本文为大家介绍两种思路实现点击页面其它地方隐藏该div,第一种是对document的click事件绑定事件处理程序.. 第一种思路分两步 第一步:对document的click事件绑定事件处理程序, ...
- 使用 CUDA 进行计算优化的两种思路
前言 本文讨论如何使用 CUDA 对代码进行并行优化,并给出不同并行思路对均值滤波的实现. 并行优化的两种思路 思路1: global 函数 在 global 函数中创建出多个块多个线程对矩阵每个元素 ...
随机推荐
- C++之构造函数的继承
#include<iostream> usingnamespace std; classBase1 { public: Base1()=default; Base1(const strin ...
- centos7上使用locate命令
https://blog.csdn.net/yqh19880321/article/details/72426879
- GMM与EM算法
用EM算法估计GMM模型参数 参考 西瓜书 再看下算法流程
- loadrunner学习笔记之参数设置
一.关于参数的定义 在你录制程序运行的过程中,脚本生成器自动生成由函数组成的用户脚本.函数中参数的值就是在录制过程中输入的实际值. 例如,你录制了一个Web应用程序的脚本.脚本生成器生成了一个声明,该 ...
- python3与mysql交互:pymysql
python3与mysql交互 1.安装pymysql模块 pip3 install pymysql3 2.pymysql的简单使用: # /usr/bin/env python3 import py ...
- PHP 中文字符串相关
1.字符串长度 中文字符串使用strlen() 获取字符串长度时一个UTF8的中文字符是3个字节长度:使用mb_strlen(),选定内码为UTF8,则会将一个中文字符当作长度1来计算 在对含中文字符 ...
- linux查询系统负载
linux uptime命令主要用于获取主机运行时间和查询linux系统负载等信息.uptime命令过去只显示系统运行多久.现在,可以显示系统已经运行了多长时间,信息显示依次为:现在时间.系统已经运行 ...
- thinkphp5.0与thinkphp3.2之间的区别
5.0版本和之前版本的差异较大,本篇对熟悉3.2版本的用户给出了一些5.0的主要区别. URL和路由 5.0的URL访问不再支持普通URL模式,路由也不支持正则路由定义,而是全部改为规则路由配合变量规 ...
- spring boot mybatis 多数据源配置
package com.xynet.statistics.config.dataresources; import org.springframework.jdbc.datasource.lookup ...
- Java 中的三大特性
我们都知道 Java 中有三大特性,那就是继承 ,封装和多态 .那我今天我就来说说这几个特性 . 老样子 ,先问问自己为什么会存在这些特性 .首先说封装 ,封装就是使用权限修饰符来实现对属性的隐藏 , ...