原文: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,没有出现“超卖”。

 

最新文章

  1. .NET异步编程之回调
  2. Hibernate &lt;二级缓存&gt;
  3. MFC编程入门之十五(对话框:一般属性页对话框的创建及显示)
  4. 问:Linux下Chrome标题栏中文乱码
  5. 显示ios设备信息的程序
  6. erlang 基础知识
  7. [C语言 - 12] Union联合
  8. 使用canvas制作在线画板
  9. 怎么打开Windows Server 2008 图片预览的功能?
  10. Java中long和double的原子性
  11. 使用MapReduce将HDFS数据导入到HBase(一)
  12. dos判断系统版本
  13. linux tomcat部署含有matlab画图打包的java web程序
  14. .htaccess 保护文件夹
  15. Bring up a Kafka-based Ordering Service
  16. linux-kernel-4.4 移植 (2)解决上部遗留DMA-PL330的问题
  17. mysql 开发进阶篇系列 53 权限与安全(账号管理的各种权限操作 上)
  18. HttpWebRequest post 请求超时问题
  19. Python - for 循环
  20. angular 生命周期钩子 ngOnInit() 和 ngAfterViewInit() 的区别

热门文章

  1. log4j记录日志到指定文件
  2. Robots.txt 不让搜索引擎收录网站的方法
  3. 1391: [Ceoi2008]order
  4. HDU - 4465 期望 + 取log优化
  5. CentOS 7.2 下 PXE+kickstart 自动安装系统
  6. php输出多余的空格或者空行
  7. GTK, GTK+, Qt, KDE, GNOME, Unity的区别与联系
  8. JS日期、时间 格式化转换方法
  9. caffe fine tune 复制预训练model的参数和freeze指定层参数
  10. imageio 载入 Buffer 格式的图片