前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >推荐11-PHP用redis解决超卖的问题

推荐11-PHP用redis解决超卖的问题

作者头像
猿哥
发布于 2019-09-19 09:18:05
发布于 2019-09-19 09:18:05
96300
代码可运行
举报
文章被收录于专栏:Web技术布道师Web技术布道师
运行总次数:0
代码可运行

前言

在商品秒杀活动中,比如商品库存只有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.订单表
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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.商品表
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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.日志表
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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步写代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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地址不能拼接上带&号的参数,否则执行失败。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-09-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 PHP技术大全 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
MySQL悲观锁:轻松解决商品超卖难题,提升电商网站稳定性!
因此,悲观锁在未通过索引条件检索数据时,会锁定整张表。导致其他程序不允许“加锁的查询操作”,影响吞吐。故如果在查询居多的情况下,推荐使用乐观锁。
Tinywan
2023/10/16
4980
MySQL悲观锁:轻松解决商品超卖难题,提升电商网站稳定性!
php redis实现秒杀抢购
对于第一个问题,已经很容易想到用缓存来处理抢购,避免直接操作数据库,例如使用Redis。
黄啊码
2020/05/29
2.5K0
thinkphp+redis实现秒杀功能
1,安装redis,根据自己的php版本安装对应的redis扩展(此步骤简单的描述一下)
OwenZhang
2021/12/08
6440
thinkphp+redis实现秒杀功能
php结合redis实现高并发下的抢购、秒杀功能的实例
下面小编就为大家带来一篇php结合redis实现高并发下的抢购、秒杀功能的实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
OwenZhang
2021/12/08
1.4K0
【秒杀系统】秒杀系统和拓展优化
框架技术: SpringBoot2.x ,Mybatis-plus ,Thymeleaf
冷环渊
2022/03/09
4.7K0
【秒杀系统】秒杀系统和拓展优化
详解thinkphp+redis+队列的实现代码
1,安装Redis,根据自己的PHP版本安装对应的redis扩展(此步骤简单的描述一下) 1.1,安装 php_igbinary.dll,php_redis.dll扩展此处需要注意你的php版本如图: 1.2,php.ini文件新增 extension=php_igbinary.dll;extension=php_redis.dll两处扩展 ok此处已经完成第一步redis环境搭建完成看看phpinfo
用户2323866
2021/07/01
4850
laravel+Redis简单实现队列通过压力测试的高并发处理
在一般的网络商城中我们会经常接触到一些高并发的业务状况,例如我们常见的秒杀抢购等活动, 在这些业务中我们经常需要处理一些关于请求信息过滤以及商品库存的问题。 在请求中比较常见的状况是同一用户发出多次请求或者包含恶意的攻击,以及一些订单的复购等情况。 而在库存方面则需要考虑超卖这种状况。 下面我们来模拟一个简单可用的并发处理。 直接上代码
BinGo_Blog
2022/11/16
1.3K0
laravel+Redis简单实现队列通过压力测试的高并发处理
微服务入门之SpringCloud(视频文案)
微服务架构风格是一种将单个应用程序作为一套小型服务开发的方法,每种应用程序都在自己的进程中运行,并与轻量级机制(通常是HTTP资源API)进行通信。 这些服务是围绕业务功能构建的,可以通过全自动部署机制独立部署。 这些服务的集中管理最少,可以用不同的编程语言编写,并使用不同的数据存储技术。
Java猫说
2019/04/11
5710
php结合redis实现秒杀功能
第一种,简单实现 <?php $conn=mysql_connect("localhost","test","123456"); if(!$conn){ echo "connect faile
用户1349575
2022/01/24
9620
电商设计手册之基础商品信息
第一篇我们主要看看一个入门的电商平台(B2C)如何去构建自己的基础商品信息,其实这个事情很简单,想想我们的现实生活,商家摆放商品到货架,客户从货架挑选商品,客户把挑选好的商品放入购物车(篮),最后客户去收银台结账。
大愚
2019/03/01
1.2K0
电商设计手册之基础商品信息
redis学习笔记
优点: 高并发读写性能、大数据量扩展(分布式存储)、配置简单、操作与数据模型灵活高效、成本 低廉
CS逍遥剑仙
2018/10/11
6730
redis学习笔记
Redis分布式事务锁的应用——秒杀、超卖 简单例子 (下)
上一篇文章介绍了Redisson的分布式锁原理,这篇文章来验证一下Redisson分布式锁的作用。
HaC
2020/12/30
1.1K0
Redis分布式事务锁的应用——秒杀、超卖 简单例子 (下)
使用Seata彻底解决Spring Cloud中的分布式事务问题!
随着业务需求的变化,单体应用被拆分成微服务应用,原来的三个模块被拆分成三个独立的应用,分别使用独立的数据源,业务操作需要调用三个服务来完成。此时每个服务内部的数据一致性由本地事务来保证,但是全局的数据一致性问题没法保证。
macrozheng
2019/11/23
2.2K0
PHP处理库存超卖的几种处理方法
第一种方法:使用mysql数据库的锁机制。在事务中使用 for update 语句,在事务处理完成之后释放这一条数据。
终有救赎
2023/12/14
2260
PHP解决高并发问题
举个例子,高速路口,1秒钟来5部车,每秒通过5部车,高速路口运作正常。突然,这个路口1秒钟只能通过4部车,车流量仍然依旧,结果必定出现大塞车。(5条车道忽然变成4条车道的感觉)
全栈程序员站长
2022/09/04
1.3K0
PHP解决高并发问题
微擎应用:安装、更新、卸载 - 变更数据表
1. 前言 2. manifest.xml 3. 安装模块执行 install.php 4. 卸载模块执行 uninstall.php 5. 模块升级时执行 upgrade.php 1. 前言
很酷的站长
2023/01/03
1.2K0
微擎应用:安装、更新、卸载 - 变更数据表
MySQL指南:全面掌握视图、触发器、权限管理和远程连接的要点与技巧
开始之前推荐一篇实用的文章:《H5 App实战四:H5 App的跨域请求与数据交互》,作者:【china马斯克】。
Lion 莱恩呀
2024/11/24
1830
MySQL指南:全面掌握视图、触发器、权限管理和远程连接的要点与技巧
秒杀安全
简介 我们通常衡量一个Web系统的吞吐率的指标是QPS(Query Per Second,每秒处理请求数),解决每秒数万次的高并发场景,这个指标非常关键。举个例子,我们假设处理一个业务请求平均响应时间为100ms,同时,系统内有20台Web服务器,配置MaxClients为500个(表示服务器的最大连接数目)。 那么,我们的Web系统的理论峰值QPS为(理想化的计算方式): 20*500/0.1 = 100000 (10万QPS) 在高并发的实际场景下,机器都处于高负载的状态,在这个时候平均响应时间
架构师小秘圈
2018/04/02
3.3K0
秒杀安全
1-3 云商城环境准备和数据库表结构
  在商城系统中我们会使用到很多基础环境,比如MySQL数据库、Nacos注册中心、Redis数据库等、这些我们都会安装在Docker容器中。所以接下来我们搭建下基础环境
用户4919348
2021/01/13
4300
1-3 云商城环境准备和数据库表结构
电商功能分析与设计[商品数据表设计]
本文针对电商系统中的商品管理模块进行分析,总结出如何设计一个合理的商品管理表。分析的角度,重点放在商品的规格设计上,针对单一规格、多规格和组合规格情况进行如何设计表的总结。
兔云小新LM
2021/07/12
2.5K0
电商功能分析与设计[商品数据表设计]
相关推荐
MySQL悲观锁:轻松解决商品超卖难题,提升电商网站稳定性!
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档