Redis队列实现秒杀

Redis实现商品秒杀 随着互联网的发展和消费者的需求越来越高,商品的销售也变得越来越激烈。而对于商家来说,最直观的解决方式即为促销活动。然而,促销活动也会引发一定的风险。如果处理得不当,可能会出现“抢购”活动中的库存不足等问题。本文将利用Redis实现商品秒杀,来避免这些问题的发生。 阅读详情

秒杀系统特点

1、抢购人数远多于库存,读写并发巨大。

2、库存少,有效写少。

3、写需强一致性,商品不能卖超。

4、读强一致性要求不高。

5、稳定性难:高并发下,某个小依赖可能直接造成雪崩、流量预期难精确,过高也造成雪崩。分布式集群,机器多,出故障的概率高。

6、准确性难:库存、抢购成功数,创建订单数之间的一致性。

7、高性能难:有限成本下需要做到极致的性能。

秒杀系统——架构原则

1、稳定性:减少第三方依赖,同时自身服务部署也需做到隔离。压测、降级、限流方案、确保核心服务可用。需健康度检测机制,整个链路避免单点。

2、高性能:缩短单请求访问路径,减少IO。减少接口数,降低吞吐数据量,请求次数减少。

秒杀服务核心实现

1、怎样设计秒杀服务:满足基本需求,做到单服务极致性能。请求链路流量优化,从客户端到服务端每层优化。稳定性建设。

2、基本需求:扣库存、查库存、排队进度。(做到单服务极致性能)。查订单详情、创建订单,支付订单。(库存少抢购人数远多于库存,读写并发高)

基本需求——扣库存方案

1、下单减库存?

并发请求——>创建订单——>扣库存——>支付 这种流程不会超卖,但问题是如果有人恶意下单不支付,占用库存。

2、支付减库存?

并发请求——>创建订单——>支付——>扣库存 这种流程是支付一次扣一次库存,如果用户把商品买完了,别的用户下不了订单或者订单超卖。

3、预扣库存?

并发请求——>扣库存——>创建订单——>支付——>10分钟内不支付取消订单,加库存。

采用预扣库存方案比较好。

wxml

<view>
  <l-countdown time-type="second" time="{{expire_time}}" bind:linend="changeBtn"/>

  <view><image src="{{Detail.image}}" bindtap="image"></image></view>
  <view>{{Detail.store_product.store_name}}</view>
  <view>{{Detail.price}}</view>

    <view>
      <l-button disabled="{{ disabled }}" bind:lintap="buyGoods" type="error" data-id="{{Detail.id}}">立即秒杀</l-button>
     </view>
</view>

js

// pages/Detail/Detail.js
Page({

  /**
   * 页面的初始数据
   */
  data: {
    expire_time:0,
    disabled:false,
    expire_time:'',

  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad(options) {
    let that = this
    let sid = options.id
    wx.request({
      url: 'http://www.wej.com/index.php/api/seckill/goodsDetail', //仅为示例,并非真实的接口地址
      data: {
        sid
      },
      header: {
        'content-type': 'application/json' // 默认值
      },
      success (res) {
        console.log(res.data) 
        let newdate = Math.round(new Date().getTime() / 1000).toString()
        let expire_time = res.data.data.start_time - newdate
        console.log(expire_time)
        that.setData({
          Detail:res.data.data,
          expire_time:expire_time
        })
        if(expire_time > 0){
          that.setData({
            disabled:true
          })
        }else{
          that.setData({
            disabled:false
          })
        }
      }
    })


  },

  buyGoods(c){
    clearTimeout(this.TimeID);
    this.TimeID = setTimeout(() => {
      let goods_id = c.currentTarget.dataset.id
      wx.request({
        url: 'http://www.wej.com/index.php/api/seckill/snap_up', //仅为示例,并非真实的接口地址
        data: {
          goods_id
        },
        header: {
          'content-type': 'application/json' // 默认值
        },
        success (res) {
          console.log(res.data)
        }
      })
    }, 1000);

  },


changeBtn(){
  console.log('秒杀开始')
    this.setData({
      disabled:false
    })
  },




image(c){
  wx.previewImage({
    current: this.data.Detail.image, // 当前显示图片的 http 链接
    urls: [this.data.Detail.image] // 需要预览的图片 http 链接列表
  })
},


})

json

{
  "usingComponents": {
    "l-countdown":"/dist/countdown",
    "l-button":"/dist/button"
  }
}
<?php

namespace App\Http\Controllers;

use App\Models\AddressInfo;
use App\Models\StoreOrder;
use App\Server\Snowflake;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;

class Seckill extends Controller
{
    /**
     * 数据预热
     * @return \Illuminate\Http\JsonResponse
     */
    public function activity()
    {
        $result = \App\Models\Seckill::with('StoreProduct')
            ->get()->toArray();
        foreach ($result as $val){
            //生成对应商品库存队列
            $goods = "activity_goods_".$val['product_id'];
            for ($i=0; $i < $val['stock']; $i++) {
                Redis::lpush($goods,1);
            }
        }
        return response()->json(['code' => 20000, 'msg' => '查询成功', 'data' => $result]);
    }

    /**、
     * 秒杀列表
     * @return \Illuminate\Http\JsonResponse
     */
    public function activityList()
    {
        $result = \App\Models\Seckill::with('StoreProduct')
            ->get()->toArray();
        return response()->json(['code' => 20000, 'msg' => '查询成功', 'data' => $result]);
    }

    /**
     * 秒杀商品详情
     * @return \Illuminate\Http\JsonResponse
     */
    public function goodsDetail()
    {
        $goods_id = request()->get('sid');
        $result = \App\Models\Seckill::with(['StoreProduct'])
            ->where('product_id',$goods_id)
            ->first();
        return response()->json(['code' => 20000, 'data' => $result, 'msg' => '查询成功']);
    }

    /**
     * 校验库存
     * @return \Illuminate\Http\JsonResponse
     */
    public function snap_up(){
        //用户ID
        $userID = 1;
        //商品ID
        $goodsID = request()->get('goods_id');
        //对应商品库存队列
        $goods = "activity_goods_".$goodsID;
        //对应商品抢购成功用户集合 {1,3,4}
        $robSuccessUser = "success_user".$goodsID;
        //进行判断当前用户是否在抢成功的队列里面
        $result = Redis::sismember($robSuccessUser,$userID);
        //如果你在这里面,就抢完了
        if ($result) {
            //如果抢购成功 返回状态码,进行下单
            return response()->json(['code' => 20000, 'data' => '', 'msg' => '已经抢购过了']);
        }
        //减库存,把队列里面的数据从左边 头
        $count = Redis::lpop($goods);
        if (!$count) {
            //如果抢购成功 返回状态码,进行下单
            return response()->json(['code' => 20001, 'data' => '', 'msg' => '已经抢光了哦']);
        }
        //把当前这个秒杀的uid存储到抢购成功的队列里set
        $success = Redis::sadd($robSuccessUser, $userID);
        if(!$success){
            //已经在成功队列里了,加回库存,防止的是同个用户并发请求
            Redis::lpush($goods, 1);
            //如果抢购成功 返回状态码,进行下单
            return response()->json(['code' => 20002, 'data' => '', 'msg' => '已经抢购过了']);
        }
        //如果抢购成功 返回状态码,进行下单
        return response()->json(['code' => 20000, 'data' => '', 'msg' => '秒杀成功']);
    }

    /**
     * 生成订单
     * @return false|\Illuminate\Http\JsonResponse|string
     */
    public function createOrder(){
        //用户ID
        $userID = request()->get('userID');
        //商品ID
        $goodsID = request()->get('goods_id');
        //地址ID
        $address_id = request()->get('address_id');
        //对应商品抢购成功用户集合
        $robSuccessUser = "success_user".$goodsID;
        //进行判断当前用户是否在抢成功的队列里面
        $result = Redis::sismember($robSuccessUser,$userID);
        //如果你在这里面,就抢完了
        if (!$result) {
            //如果抢购成功 返回状态码,进行下单
            return response()->json(['code' => 20003, 'data' => '', 'msg' => '手慢了!']);
        }
        DB::beginTransaction();
        try{
            //减库存
            $shopData = \App\Models\Seckill::with('StoreProduct')
                ->where('product_id',$goodsID)
                ->first()->toArray();
            $shopStock = \App\Models\Seckill::where('product_id',$goodsID)->update(['stock' => $shopData['stock'] - 1]);
            if (!$shopStock) return json_encode(['code' => 50000,'msg' => '下单失败','data' => []]);
            //地址
            $address_info = AddressInfo::find($address_id)->toArray();
            //生成订单
            Snowflake::machineId($userID);
            $order_id = substr(date('Ymd'),2).'-'.Snowflake::createOnlyId();
            $data = [
                'order_id' => $order_id,
                'uid' => $userID,
                'real_name' => $address_info['real_name'],
                'user_phone' => $address_info['phone'],
                'user_address' => $address_info['detail'],
                'pay_price' => $shopData['store_product']['price'],
                'pay_time' => time()
            ];
            $orderAdd = StoreOrder::insert($data);
            if (!$orderAdd) return json_encode(['code' => 50000,'msg' => '下单失败','data' => []]);
            DB::commit();
            //下单成功,跳转支付页面
            return response()->json(['code' => 20000, 'data' => '', 'msg' => '下单成功!']);
        }catch (\Exception $e){
            DB::rollBack();
            return response()->json(['code' => 50000, 'data' => '', 'msg' => $e->getMessage()]);
        }
    }


}

秒杀抢购案例,基于 Redis 实现 第一个线程在操作后,数据库中的version变成了2,但是他自己满足version=1 ,所以没有问题,此时线程2执行,线程2 最后也需要加上条件version =1 ,但是现在由于线程1已经操作过了,vsersion 的值已经变为了 2;,那么假设在服务器A的tomcat内部,有两个线程,这两个线程由于使用的是同一份代码,那么他们的锁对象是同一个,是可以实现互斥的,但是如果现在是服务器B的tomcat内部,又有两个线程,但是他们的锁对象写的虽然和服务器A一样,但是锁对象却不是同一个,所以。 阅读详情

相关推荐

laravel实现redis队列秒杀

控制器层 秒杀前做的准备我们需要把数据库的数据引入到你得redis里面 同步到redis里面 然后就可以进行秒杀 public function before(){ $arrs = DB::select("select id,stock from 数据表名称");//Db查id和库存 $data = json_decode(json_encode($arrs),true); $redis=new \Redis();//初始化redis $redis->connect('127.0.0

weixin_48132808的博客 861

使用 Redis 实现秒杀系统

秒杀系统是指在一个非常短的时间内(通常是几十秒钟),将某种商品或服务以极低的价格进行销售。这种销售方式需要保证高并发和高可用性,同时防止超卖和恶意攻击等问题。秒杀系统的特点是大量的用户在同一时间瞬间涌入服务器,该类型的高并发读写操作对系统性能提出了较高的要求。Redis(Remote Dictionary Server)是一个开源、支持网络、基于内存、键值对存储数据库。它支持多种数据结构,如字符串(String)、哈希(Hash)、列表(List)、集合(Set)和有序集合(Sorted Set)。

BXA 6296

给汽车芯片做‘体检’:手把手教你用IEC62380和SN29500估算功能安全失效率

本文详细介绍了如何使用IEC62380和SN29500标准估算汽车芯片的功能安全失效率,涵盖基础认知、方法论对比及实战计算。通过具体案例演示两种标准的应用流程,帮助工程师准确评估半导体器件的可靠性,确保符合ISO 26262标准要求。

weixin_28725553的博客 460

redis之:秒杀功能

【代码】redis之:秒杀功能。

我智商开挂的博客 1415

wpf入门第五篇 WPF with ECharts

前言 本文是wpf入门系列第五篇,面向有winform或者web前端基础的、并且也有C#基础的同学。 ECharts是百度团队开发的开源免费的js图表控件库,非常强大,文档齐全。WPF中使用图表,除了可以选择devexpress或者oxyplot之类的专为wpf开发的图表控件之外,还可以考虑使用 WebBrowser+ECharts 来完成复杂的图表展示,其展示效果不亚于前者。 本文介绍了 WP...

wf824284257的博客 1万+

ElementUI中的el-table解决宽度问题 - 根据内容自动撑开

在使用element-ui中,会发现表格组件el-table在未指定宽度情况下,会自动计算并给表格宽度赋值。但实际开发中,有时需要根据内容实际长度自动撑开显示,由内容的多少而决定表格的宽度,而不是默认宽度为100%。在默认情况下表格宽度为100%显示,并且列项未指定宽度情况下,剩余部分会平均分配宽度,本案例实际需求是由内容自动撑开显示。componentUpdated

觉醒法师的专栏 1万+

5. Redis优化秒杀、Redis消息队列实现异步秒杀

秒杀业务的优化思路是什么?变同步下单为异步下单同步下单:请求来了之后判断有没有资格,有资格就自己下单,由于各种原因,耗时非常久异步下单:业务分成两部分,一部分对于抢购资格的判断,如果有资格就立即结束,返回订单号;另一部分执行耗时较久的业务先利用Redis完成库存余量、一人一单判断,完成抢单业务再将下单业务放入阻塞队列,利用独立线程异步下单基于堵塞队列的异步秒杀存在哪些问题?内存限制问题。

weixin_51351637的博客 2217

【图像检测】基于MATLAB的火灾监测系统设计

1 简介 针对传统火灾监测系统对于大空间的室内场合和开阔的室外环境易失效的问题,提出了一种结合火灾火焰特征和烟雾特征来进行判断的数字图像型火灾监测算法.火焰颜色特征是基于RGB颜色模型中的R,G,B三基色分量和它们之间的关系来判断是否有火焰发生,烟雾模型特征是图像灰度化预处理后,基于HIS空间模型提取的,通过计算像素点与基准像素点之间距离D的大小来判断是否有火灾发生.对提出的算法进行MATLAB仿真,并和传统的基于五种算子的边缘检测算法提取火焰烟雾特征进行对比.实验结果表明,提出的火焰特征和烟雾特征提取算

qq_59747472的博客 2797

Redis队列实现java秒杀系统,无脚本,可用于生产

写在前面: 需求是做一个秒杀系统,比如大家来抢100台手机,先到先得。 查阅了网上很多用redis实现秒杀的demo(java语言)。 竟然没一个能用的!!! 有些是php的,没闲心研究了,现在说说为什么不能用。 绝大多数的DEMO都是基于redis的watch特性的事务实现①, 个别是基于redis分布式锁实现②。 当然还有些用了脚本的,我也没仔细看是lua还是调用redis指令,哪有那个闲心去...

拒绝八股文从我做起 2455

LLCC68芯片系统架构与LoRa驱动开发核心解析

LoRa射频收发器是低功耗广域物联网(LPWAN)的关键组件,其本质是集成了调制解调器、协议引擎、数据缓冲区与电源管理的专用SoC。理解其工作模式状态机(Sleep/Standby/TX/RX)与SPI主机接口时序规范,是实现稳定通信的基础。LLCC68通过寄存器映射与命令集双接口抽象硬件控制,支持LoRa扩频调制与FSK兼容模式,具备高链路预算(157 dB)和DC-DC/LDO动态电源切换能力。典型应用场景包括远距离传感器节点、智能表计与工业无线监测系统。本文深入剖析LLCC68的信号链路径、中断机制与

weixin_29859471的博客 284

1、使用Spark SQL查询Databricks:全面指南

本博客全面介绍了如何使用Spark SQL在Databricks平台上进行高效的数据查询与分析。内容涵盖SQL基础操作、数据过滤、复杂查询技巧、简单与高级计算、数据聚合、日期处理等核心主题,适合数据分析师、开发人员及SQL学习者。通过逐步讲解和实用示例,帮助读者掌握从入门到高级的SQL技能,并提供学习路径建议和免费代码资源,助力实际数据分析工作。

redis7keeper的博客 91

Redis队列秒杀实现

&lt;?php //首先,加载一个Reids组件, $redis = new Redis(); $resid-&gt;connect('127.0.0.1', 6379); $reds_name = 'miaosha'; for ($i = 0;$i &lt; 100;$i++) { $uid = rand(1000000, 9999999); } //接收用户的id, //$uid...

xq1q1的博客 882

基于非接触式微波雷达设计的睡眠监控系统_323

本项目设计了一种基于非接触式微波雷达的睡眠监控系统,通过高精度的生理信号采集与分析,帮助用户全面了解自身的睡眠状态和健康状况。系统采用60GHz频段的R60ABD1毫米波雷达模块,实现对人体呼吸频率、心率、活动状态、体动幅度等参数的非接触式测量;结合MLX90614红外体温传感器,能够实时监测人体体温。此外,系统通过蜂鸣器报警和数据上云功能,及时向用户反馈异常生理指标,确保健康风险早期预警。数据通过ESP8266-WIFI模块上传至华为云物联网平台,用户可通过Android手机APP和Windows上位机远

503

秒杀优化(异步秒杀,基于redis-stream实现消息队列)

异步秒杀,基于redis-stream实现消息队列

2301_79748665的博客 1514

【纯干货分享】常用高速接口眼图模板合集

文章汇集了常见的高速接口的信号眼图测试模板技术要求,比如,LPDDR5/4、PCIe 5.0/4.0、USB、MIPI等等,方便工程师快速进行查询和使用。

一只豌豆象的博客 3119

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

redis 队列实现秒杀/抢购

消息队列的应用场景例如:秒杀、抢单功能。 下面写个Demo简单实现一下秒杀,也就是抢购。 首先创建一个lpush.html文件,代码如下: &lt;!DOCTYPE html&gt; &lt;html&gt;     &lt;head&gt;         &lt;title&gt;TODO supply a title&lt;/title&gt;         &lt;meta cha...

weixin_34824012的博客 3632

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

TP5 Redis队列实现秒杀

实现秒杀前请确认php成功安装redis扩展! <?php namespace app\home\controller; use Redis AS Rediss; class Redis extends Cauth{ public function index(){ //首先,加载一个Reids组件, $redis = new Rediss();...

风吹无痕,雨过无迹 6863

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Redis消息队列实现异步秒杀

Redis Stream 是一种专门为消息队列设计的数据类型,它支持消息可回溯、多消费者消费、消息确认机制等特性。通过使用 Redis Stream 构建消息队列,我们能够将部分业务逻辑进行异步处理。当用户发起请求时,系统迅速将请求信息放入消息队列,无需等待后续处理完成即可返回响应,大大提高了系统的响应速度。同时,多个消费者可以并行从消息队列中获取任务进行处理,有效提升了系统的吞吐量,确保系统在高并发场景下仍能稳定运行。

hello77的blogggg 祝你得偿所愿 1136

python redis队列实现秒杀_Redis 实现秒杀

导语秒杀想必大家都了解,在短时间内请求访问会激增,同时要保证不会超卖和数据的准确,对于技术方面还是有些考验的。可惜的是,一直没有机会在项目中实现。再看了一些资料后,打算实验下。以下代码仅为测试所用,环境比较简单,请根据实际情况进行修改。创建秒杀队列在开始秒杀之前,先将商品放入队列中,如下/*** 创建秒杀列表*/public function createList(){$count = 30;$r...

weixin_39794734的博客 483

redis队列实现秒杀

package demo; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; import java.util.concurrent.ExecutorService; import java.util.concu...

baidu_36611723的博客 573

微服务Spring Boot 整合Redis 阻塞队列实现异步秒杀下单

Redis 秒杀下单如何进行性能优化,通过 阻塞队列实现Redis异步秒杀下单优化!

wanghuichen的博客 9771

【Redis实战篇】Redis三种方法实现消息队列优化秒杀优惠券

Redis Stream 是 Redis 5.0 版本引入的新数据结构,主要用于实现消息队列(MQ,Message Queue)。Redis 原本的发布订阅(pub/sub)虽能实现消息队列功能,但存在消息无法持久化的问题,一旦出现网络断开、Redis 宕机等情况,消息就会被丢弃。而 Redis Stream 提供了消息的持久化和主备复制功能,可让任何客户端访问任何时刻的数据,能记住每个客户端的访问位置,还能保证消息不丢失。

MonKingWD的博客 1427

黑马点评_Redis消息队列实现异步秒杀

日常生活中,我们简单想一下,可能我们作为消费者不想有快递柜,想让快递员送货上门,但是如果我们在班上,快递到了还要跑回家签收,其实很不合理,所以快递柜的存在对于快递员和我们消费者都是有利的。解耦性‌:消息队列实现了发送方和接收方的解耦,发送方只需将消息发送到队列中,不需要直接知道接收方的存在或状态,接收方通过订阅队列来获取消息,而不需要关心消息的发送者。比如你开启两个窗口,一个使用BRPOP,如果消息队列中没有数据,会阻塞住,另外一个窗口如果输入进出数据了,卡住的窗口会解除阻塞,输出被输入的数据。

buyaotutou的博客 903

利用 Redis 队列操作的原子性实现秒杀

添加一个队列模拟商品列表 lpush productlist 1 2 3 4 5 6 7 8 9 10 利用多线程模拟 30 个人抢购这 10 件商品: package demo; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import redis.clien...

. 1万+

基于Redis的Stream消息队列实现异步秒杀

这是秒杀逻辑的入口,负责调用 Redis 中的 Lua 脚本以完成高并发场景下的秒杀操作,包含库存校验、扣减库存、用户下单记录以及异步下单的消息发送。通过全局唯一的订单 ID 和 Lua 脚本原子性,确保秒杀操作在高并发下的性能与一致性,并根据脚本返回结果快速响应用户秒杀请求。4.完成下单项目启动时开启一个线程任务,尝试获取消息队列中的订单信息。

Xwz的博客 1064

微服务Spring Boot 整合Redis 基于Redis的Stream 消息队列 实现异步秒杀下单

Redis 实现秒杀下单,采用Stream消息队列实现,快!

wanghuichen的博客 8110
上一篇: 雪花算法(PHP)
下一篇: Vscode连接远程服务器(一套配置成功)
陈卿诺语
博客等级 码龄5年 167粉丝 60原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值