原标题:PHP用redis解决超卖的问题

前言

在商品秒杀活动中,比如商品库存只有100,但是在抢购活动中可能有200人同时抢购,这样就出现了并发,在100件商品下单完成库存为0了还有可能继续下单成功,就出现了超卖。

为了解决这个问题,今天我主要讲一下用redis队列的方式处理。redis有list类型,list类型其实就是一个双向链表。通过lpush,pop操作从链表的头部或者尾部添加删除元素。这使得list即可以用作栈,也可以用作队列。先进先出,一端进,一端出,这就是队列。在队列里前一个走完之后,后一个才会走,所以redis的队列能完美的解决超卖并发的问题。

解决秒杀超卖问题的方法还有比如:1.使用mysql的事务加排他锁来解决;2.使用文件锁实现。3.使用redis的setnx来实现锁机制等。

实现原理

将商品库存循环lpush到num里,然后在下单的时候通过rpop每次取出1件商品,当num的值为0时,停止下单。

第1步创建表

一共有三张表,分别是:订单表、商品表、日志表。

1.订单表 CREATE TABLE `ims_order` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`order_sn` char(32) NOT NULL,

`user_id` int(11) NOT NULL,

`status` int(11) NOT NULL DEFAULT '0',

`goods_id` int(11) NOT NULL DEFAULT '0',

`sku_id` int(11) NOT NULL DEFAULT '0',

`number` int(11) NOT NULL,

`price` int(10) NOT NULL COMMENT '价格:单位为分',

`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,

PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=5820 DEFAULT CHARSET=utf8 COMMENT='订单表' 2.商品表 CREATE TABLE `ims_hotmallstore_goods` (

`id` int(11) unsigned NOT NULL AUTO_INCREMENT,

`name` varchar(50) NOT NULL COMMENT '商品名称',

`type_id` int(11) NOT NULL COMMENT '商品分类',

`img` text NOT NULL COMMENT '商品图片',

`money` decimal(10,2) NOT NULL COMMENT '售价',

`money2` decimal(10,2) NOT NULL COMMENT '原价',

`is_show` int(11) NOT NULL DEFAULT '1' COMMENT '1.上架2.下架',

`uniacid` int(11) NOT NULL COMMENT '小程序id',

`inventory` int(11) NOT NULL COMMENT '库存',

`details` text NOT NULL COMMENT '详情',

`store_id` int(11) NOT NULL COMMENT '商家id',

`sales` int(11) NOT NULL COMMENT '销量',

`logo` varchar(100) NOT NULL,

`num` int(11) NOT NULL,

`is_gg` int(11) NOT NULL DEFAULT '2' COMMENT '是否开启规格',

PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 3.日志表 CREATE TABLE `ims_order_log` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`status` int(11) NOT NULL DEFAULT '0',

`msg` text CHARACTER SET utf8,

`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,

PRIMARY KEY (`id`),

KEY `status` (`status`)

) ENGINE=InnoDB AUTO_INCREMENT=4562 DEFAULT CHARSET=gb2312 COMMENT='订单日志表' 第2步写代码 class Test {

private static $instance = null;

// 用单列模式 实例化Redis

public static function Redis

{

if (self::$instance == null) {

$redis=new Redis;

$redis->connect('127.0.0.1',6379);

self::$instance = $redis;

}

return self::$instance;

}

// 将商品库存循环到lpush的num里

public function doPageSaveNum

{

$redis=self::Redis;

$goods_id=1;

$sql="select id, num, money from ims_hotmallstore_goods where id=".$goods_id;

$goods=pdo_fetch($sql);

if(!empty($goods)){

for($i=1; $i<=$goods['num']; $i++){

$redis->lpush('num',$i);

}

die('成功!');

}else{

$this->echoMsg(0,'商品不存在。');

}

}

// 抢购下单

public function doPageGoodsStore

{

$goods_id=1;

$sql="select id, num, money from ims_hotmallstore_goods where id=".$goods_id;

$goods=pdo_fetch($sql);

$redis=self::Redis;

$count=$redis->rpop('num');//每次从num取出1

if($count==0){

$this->echoMsg(0,'no num redis');

}

$this->doPageGoodsOrder($goods,1);

}

// 保存日志

public function echoMsg($status,$msg,$_data="")

{

$data=json_encode(array('status'=>$status,'msg'=>$msg,'data'=>$_data),JSON_UNESCAPED_UNICODE);

$order_log['status']=$status;

$order_log['msg']=$msg;

$order_log['create_time']=time;

pdo_insert('order_log',$order_log);

die($data);

}

public function orderNo

{

return date('ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid, 7, 13), 1))), 0, 8);

}

// 下单更新库存

public function doPageGoodsOrder($goods,$goods_number)

{

$orderNo=$this->orderNo;

$number=$goods['num']-$goods_number;

if($number<0){

$this->echoMsg(0,'已没有库存');

}

$user_id=rand(1,500);

$order['user_id']=$user_id;

$order['goods_id']=$goods['id'];

$order['number']=$goods_number;

$order['price']=$goods['money'];

$order['status']=1;

$order['sku_id']=2;

$order['order_sn']=$orderNo;

$order['create_time']=date('Y-m-d H:i:s');

pdo_insert('order',$order);

$sql="update ims_hotmallstore_goods set num=num-".$goods_number." where num>0 and id=".$goods['id'];

$res=pdo_query($sql);

if(!empty($res)){

$this->echoMsg(1,'库存扣减成功'.$number);

}

$redis=self::Redis;

$redis->lpush('num',$goods_number);

$this->echoMsg(0,'库存扣减失败'.$number);

}

}

// 调用--将商品库存循环到lpush的num里

if($_GET['i']==1){

$model = new Test;

$model->doPageSaveNum;

}

// 调用--高并发抢购下单

if($_GET['i']==2){

$model = new Test;

$model->doPageGoodsStore;

} 第3步并发测试

1.先手动执行: http://127.0.0.1/wqchunjingsvn/web/index.php?i=1 ,将商品库存循环保存到lpush的num里。

2.这里我用Apache的ab测试,安装方法本文最后做补充。打开终端,然后执行: ab -n 1000 -c 200 http://127.0.0.1/wqchunjingsvn/web/index.php?i=2

(-n发出1000个请求,-c模拟200并发,请求数要大于或等于并发数。相当1000人同时访问,后面是测试url )

3.观察是否执行成功,执行结果如下图,说明执行成功。

第4步查看数据表

1.查看订单表,总订单数量为100,如下图,没问题。

2.查看商品库存,已经由原来的100变成0,也没问题。

3.查看日志表,总共137条记录,其中status为1的只有100条,也没问题。

总结分析

1.方案可行,库存为0,没有出现超卖。

2.用Apache的ab测试高并发时需要注意Url地址不能拼接上带&号的参数,否则执行失败。返回搜狐,查看更多

责任编辑:

php redis超卖,PHP用redis解决超卖的问题相关推荐

  1. 使用Redis分布式锁处理并发,解决超卖问题

    使用Redis分布式锁处理并发,解决超卖问题 参考文章: (1)使用Redis分布式锁处理并发,解决超卖问题 (2)https://www.cnblogs.com/VitoYi/p/8726070.h ...

  2. redis mysql 解决超卖_Redis 分布式锁解决超卖问题

    Redis 分布式锁解决超卖问题 1,Redis 事物介绍 1. Redis 事物是可以一次执行多个命令, 本质是一组命令的集合. 2. 一个事务中的所有命令都会序列化, 按顺序串行化的执行而不会被其 ...

  3. 07: redis分布式锁解决超卖问题

    07: redis分布式锁解决超卖问题 参考文章: (1)07: redis分布式锁解决超卖问题 (2)https://www.cnblogs.com/xiaonq/p/12328934.html 备 ...

  4. 基于秒杀系统解决超卖、限流、Redis限时抢购等问题

    完整项目请见:https://gitee.com/JiaBin1 一.什么是秒杀 秒杀最直观的定义:在高并发场景下而下单某一个商品,这个过程就叫秒杀 [秒杀场景] 火车票抢票 双十一限购商品 热度高的 ...

  5. mysql 超卖_mysql 解决超卖问题的锁分析

    解决超卖问题,常见的方式,利用redis 的原子性去递减:利用队列,队列入队计数.或者直接打到mysql 层.由mysql 保证不超卖,有几个玩法.利用属性不一样,挺有意思,记录下. 首先,mysql ...

  6. 秒杀系统优化以及解决超卖问题

    问题描述 在众多抢购活动中,在有限的商品数量的限制下如何保证抢购到商品的用户数不能大于商品数量,也就是不能出现超卖的问题:还有就是抢购时会出现大量用户的访问,如何提高用户体验效果也是一个问题,也就是要 ...

  7. 秒杀系统并发情况下解决超卖问题

    非分布式秒杀系统 并发情况下解决超卖问题 乐观锁防止超卖 / 令牌桶限流/ redis缓存 /接口限流/接口加盐/单用户限制访问频率/消息队列异步处理订单 #数据库表drop table if exi ...

  8. 秒杀如何解决超卖和重复购买问题

    举一个简单的例子,一个商品限购10人 使用jmeter测试 发现是乱的....当然了 我们可以通过 synchronized 来解决 的确是可以的,但是这样真的太慢了.不建议这么去弄. ======= ...

  9. 超好用的Redis管理及监控工具

    超好用的Redis管理及监控工具,使用后可大大提高你的工作效率! Redis做为现在web应用开发的黄金搭担组合,大量的被应用,广泛用于存储session信息,权限信息,交易作业等热数据.做为一名有1 ...

  10. 超神之路-Redis

    一.基础 1.简单介绍下Redis C写的开源高性能非关系型键值对数据库.底层采取epoll读写速度非常快,大多用于缓存,也提供了事务.持久化.集群以及多种数据类型的功能. 2.你认为Redis有哪些 ...

最新文章

  1. PowerPoint发布及链接图片的处理
  2. [转]十天学习PHP之第一天(PHP)----基础知识
  3. xubuntu19.10碰到initramfs终极解决方案
  4. 【bzoj2989】数列 KD-tree+旋转坐标系
  5. CSS彻底研究(3) - 浮动,定位
  6. 编写自己的Windows Live Writer插件
  7. qpython能使用json吗l_现在还能使用土墙吗?
  8. Head First Java ——我的Java入门书
  9. kaggle无法注册(含解决方案)
  10. 录音文件下载_录音啦(文字语音转换)软件安装教程
  11. ubuntu安装linux deepin,如何在ubuntu上安装如何在Ubuntu上安装Deepin桌面环境
  12. Check Point R80.10 SmartConsole汉化生成中文报表
  13. MySQL曹操外卖项目--数据库设计
  14. STM32F7 DSP库 FFT过程记录
  15. 22.扩展.等保测评相关实务
  16. 微软赵立威:云计算技术是移动互联网开发核心
  17. 国家信息安全水平考试(NISP 续)
  18. 基于贝叶斯算法文本分析之新闻分类
  19. 小tips --atoi函数 atof函数
  20. C语言for循环必备练习题

热门文章

  1. Runtime Error! R6025-pure virtual function call 问题怎么解决
  2. 电脑如何进入bios
  3. 新东方雅思词汇---8.1、reckon
  4. python_判断是否回文
  5. 数字时代带给孩子们的只有“冰冷”的科技吗?_数字体验_新浪博客
  6. 计算机的发展阶段器件,计算机发展的四个阶段构成计算机的电子元器件分别是什么?...
  7. ​给想闷声发财的小伙伴35条忠告
  8. Web前端开发(一)--html基本结构,基本标签
  9. [建筑设计].TLF-SOFT-SOFTPLAN.V13.33.bin Flaresim
  10. dialog沉浸式状态栏android,Dialog全屏,去掉状态栏的方式