Redis项目实战——优惠券秒杀

news2024/11/18 22:24:27

目录

  • Redis自增功能解决全局唯一ID
  • Redis实现优惠券秒杀的主要思路
  • 实现过程中出现的问题及解决方法
    • 超卖问题
      • 方案1 悲观锁
      • 方案2 乐观锁
    • 一人一单问题
      • 分布式锁
        • 如何用Redis实现分布式锁?
    • Redis优化秒杀
    • 消息队列实现异步秒杀
      • List
      • 发布订阅模式
      • Stream

Redis自增功能解决全局唯一ID

  • 如果用MySQL的自增长ID,ID的规律性太明显,会暴漏一些信息(比如销量等)
  • 数据量太大时一张表存不下,需要多张表,MySQL多张表的自增长都是独立的,会出现重复ID
  • 需要一种在分布式系统下可以生成全局唯一ID的工具,必须唯一且递增
  • 在某项目里,不管数据库的表有多少个,Redis只有一个,因此Redis递增功能生成的ID一定是全局唯一的
  • 为了保证递增的同时且没有规律,保证安全性,可以在Redis自增数值的基础上拼接一些其它信息
    在这里插入图片描述

Redis实现优惠券秒杀的主要思路

在这里插入图片描述

实现过程中出现的问题及解决方法

超卖问题

  • 在高并发场景下,多个线程同时操作共享的资源(库存),导致实际卖出的数量超出了库存数量

方案1 悲观锁

  • 态度比较悲观,认为线程安全问题肯定会发生,在操作数据之前提前获取锁
  • 例子:Synchronized、Lock
  • 优点:安全性高
  • 缺点:性能低,实现简单

方案2 乐观锁

  • 态度比较乐观,认为线程安全问题不一定会发生,因此不加锁,只在数据更新时去判断在它之前有没有其它线程修改数据。如果没有修改认为是安全的,直接更新数据,如果已经被修改说明不安全,重试或报异常
  • 版本号法:给库存增加一个版本字段,线程1查询并记录下库存和版本号,然后将库存-1,版本号+1,来表示线程1修改了一次数据,然后在更新数据之前再判断一下版本号,是否是自己当时记录的版本号+1,若是,说明没有并发线程在期间修改过数据,安全,可以放心更新,若不是,说明正好有并发线程在期间修改过了数据,不安全,重试或者报异常
  • CAS法:版本号法的简化版本,去掉版本号这个多余的字段,直接用库存本身代替版本号,根据库存本身有没有发生变化来确定是否更新
  • 优点:性能高
  • 缺点:实现复杂

一人一单问题

  • 常见的业务问题,要求同一个优惠券,一个用户只能下一单
  • 在库存充足判断成功后再增加一个判断,用用户ID和优惠券ID联合查询,来判断该用户是否已经买过一优惠券
  • 在单机模式下,可以加Synchronized锁来保证线程安全
  • 在集群模式下,Synchronized锁无效,需要用分布式锁来确保线程安全。Synchronized锁无效的原因是因为每台服务器有自己的常量池,锁监视器便保存在常量池中,用户尝试获取锁便是访问锁监视器,因此,主要问题是因为多个服务器的锁监视器是独立的,所以多个服务器上的用户能在同一时刻同时获取锁,进而导致线程安全问题

分布式锁

  • 在单机情况下,只有一个JVM,JVM中只有一个锁监视器,只有一个程序可以获取到锁。但在集群情况下,有多个JVM,多个JVM中有多个锁监视器,程序可以获取到多个锁,甚至同一个程序也可以获得多个锁,就会出现线程安全问题
  • 需要在多个JVM之外做一个共享的 多进程可见的 互斥的 锁监视器——分布式锁
  • 实现分布式锁的三大方式:MySQL、Redis、Zookeeper,MySQL和Zookeeper比Redis安全性更好,Redis性能比二者更好
    在这里插入图片描述

如何用Redis实现分布式锁?

  • 获取互斥锁:SET lock thread1 NX EX 10,NX是互斥,确保只有一个线程可以获取到锁,EX是设置超时时间。
  • 释放锁:直接手动删除。
  • 死锁问题:若获取到锁后线程宕机,容易出现死锁,应该增加过期时间,超时自动释放锁。
  • 误删问题:若线程1获取到锁,但业务执行时间过长,超过了TTL,会自动释放锁,此时线程2尝试获取锁成功,并正常执行业务,但期间线程1业务执行完毕,正常执行释放锁操作,此时就会把线程2的锁误删。为了避免这种情况,应该在获取锁时增加一个标识,来表示谁占有了这个锁,且只有它才有资格释放锁,因此在释放锁之前需要增加判断步骤
  • 基于setnx实现的分布式锁存在的问题:不可重入(同一个线程无法多次获取同一把锁),不可重试(获取锁只尝试一次,失败不会重试),超时释放(业务执行耗时较长会导致锁释放,存在安全隐患)
  • Redission组件:Redis基础上实现的分布式工具集合

Redis优化秒杀

  • 优化主要思路:将涉及到数据库的减库存创建订单等耗时操作用异步独立线程慢慢做,Redis只需要判断用户有没有抢成功并返回结果
  • 原来的秒杀流程:主要是Tomcat里面的一系列操作,有四个会直接操作数据库,耗时非常久。相当于一个饭店,来了一位顾客,派了一个服务员为这位顾客一条龙服务,从点菜(查询秒杀资格)到做饭(减库存和创建订单)都是这一个服务员做,效率非常低下。
  • 优化后的秒杀流程:在NGINX和Tomcat之家增加Redis,用于判断该用户能不能抢上优惠券,并将判断结果和优惠券id、用户id、订单id一起保存到阻塞队列,然后Tomcat从队列中读取消息,进行比较耗时的减库存和创建订单操作
    在这里插入图片描述
  • 其中Redis判断秒杀库存的操作可以封装到Lua脚本中执行,以确保该操作的原子性
    在这里插入图片描述
  • 基于阻塞队列的异步秒杀存在的问题?
  • 阻塞队列用的时JDK的,会占用JVM内存,大量消息会造成内存溢出

消息队列实现异步秒杀

  • 消息队列:存储管理消息
  • 生产者:发送消息到消息队列
  • 消费者:从消息队列获取消息并处理消息
  • Redis实现消息队列的三种方式:List、发布订阅模式、Stream

List

  • 链式的双端队列,LPUSH存,RPOP取,但并没有阻塞效果(队列空时不会阻塞等待),BRPOP有阻塞效果。
  • 优点:独立于JVM存在,不占JVM内存,不担心上限,且可以持久化,还能保证消息有序性
  • 缺点:无法避免消息丢失,只支持一对一

发布订阅模式

  • 消费者订阅一个或多个channel,生产者向对应channel发送消息
  • 优点:支持一对多,一个生产者可以把消息发给多个消费者。天生支持阻塞
  • 缺点:不支持数据持久化,无法避免消息丢失,消息堆积有上限

Stream

  • 优点:消息可回溯,支持一对多,支持阻塞读取
  • 缺点:可能会漏读消息
  • 消费者组:将多个消费者划分到一个组中,监听同一个消息队列,那么多个消费者就会竞争这些消息,可以加快处理消息的速度,避免消息堆积。消费者组还会维护一个标识,记录最后一个被处理的消息,可以很快恢复突发情况,避免漏读消息。此外,消费者拿到消息后,Redis并不会直接不管这条消息,而是将消息置为pending状态,表示这条消息取上了但还没处理完,处理完后通过XACK确认消息,标记为已处理,此时Redis才会放心地把消息从队列中移除,可以防止消息丢失。
  • 消费者组优点:消息可回溯,可以多消费者争抢消息,加快消费速度,可以阻塞读取,不会漏读消息,有消息确认机制,保证消息至少被消费一次

三种消息队列对比总结
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/956085.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

通过RISC-V预认证解决方案应对功能安全挑战

安全之安全(security)博客目录导读 2023 RISC-V中国峰会 安全相关议题汇总 说明:本文参考RISC-V 2023中国峰会如下议题,版权归原作者所有。

Nuxt3_2_SEO and Meta+Transitions

1. SEO and Meta 使用强大的head配置、可组合组件和组件来改善nuxt应用的SEO。 nuxt开箱即用,提供了相同的默认值,如果需要,你可以覆盖这些默认值。 charset: utf-8viewport: widthdevice-width, initial-scale1 可以在nuxt.config.ts中进…

Unity3D 连接 SQLite 作为数据库基础功能【详细图文教程】

一、简单介绍一下SQLite的优势(来自ChatGPT) 轻量级: SQLite是一个嵌入式数据库引擎,它的库文件非常小巧,没有独立的服务器进程,适用于嵌入到其他应用程序中,对于轻量级的项目或移动应用程序非常适用。零配…

云原生Kubernetes:K8S概述

目录 一、理论 1.云原生 2.K8S 3.k8s集群架构与组件 二、总结 一、理论 1.云原生 (1)概念 云原生是一种基于容器、微服务和自动化运维的软件开发和部署方法。它可以使应用程序更加高效、可靠和可扩展,适用于各种不同的云平台。 如果…

执行公开网数据采集-技术人员撤退

首先逼逼,此贴仅为秀肌肉,技术人员想学习的话可以绕道了 打开控制台,看cookie,ST,某数 第一个请求412,看VM 然后就是替换js,hook,之类的,扣代码流程,此处省…

C语言:函数原型声明时的参数列表

相关阅读 C语言专栏https://blog.csdn.net/weixin_45791458/category_12423166.html 在C语言中,使用函数前,要么对函数进行了定义,要么对函数原型进行了声明,ANSI C形式的函数原型声明形式如下: void show(char ch, …

nvm use node版本无效问题

没想到使用nvm还折腾一上午,安装nvm 1.1之后,发现 nvm install 16.20.2 nvm use 16.20.2 之后,node -v 根本不生效,找了很久发现少设置了一些变量,可以参考如下前人经验:nvm use 命令失效 - 简书 (jians…

成都优优聚优质美团服务机构!

成都优优聚是一家专业的美团代运营服务机构,其优秀的团队和丰富的经验使其成为了众多商家的首选合作伙伴。下面就让我们一起来了解一下成都优优聚做美团代运营的优势和特点。 首先,成都优优聚拥有一支专业高效的运营团队。团队成员均具备丰富的美团运营经…

2022年03月 C/C++(五级)真题解析#中国电子学会#全国青少年软件编程等级考试

第1题:数字变换 给定一个包含 5 个数字(0-9)的字符串, 例如 “02943”, 请将“12345”变换到它。 你可以采取 3 种操作进行变换 (1)交换相邻的两个数字 (2)将一个数字加 …

ssm学生公寓管理系统的设计与实现

ssm学生公寓管理系统的设计与实现106 开发工具:idea 数据库mysql5.7 数据库链接工具:navcat,小海豚等 技术:ssm 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归…

d3dcompiler_43.dll丢失怎么修复,分享几种修复d3dcompiler_43.dll的方法

不少人可能看到d3dcompiler_43.dll这个文件会感觉到陌生,是的,因为这个文件一般来说是很少丢失的,但是还是会出现d3dcompiler_43.dll丢失的情况的,今天主要是来给大家详细的说说d3dcompiler_43.dll丢失怎么修复的相关方法。 一.分…

Python Flask Web开发二:数据库创建和使用

前言 数据库在 Web 开发中起着至关重要的作用。它不仅提供了数据的持久化存储和管理功能,还支持数据的关联和连接,保证数据的一致性和安全性。通过合理地设计和使用数据库,开发人员可以构建强大、可靠的 Web 应用程序,满足用户的…

SpringBoot 2.7 集成 Netty 4 实现 UDP 通讯

文章目录 1 摘要2 核心 Maven 依赖3 核心代码3.1 服务端事务处理器(DemoUdpNettyServerHandler)3.2 服务端连接类(InitUdpNettyServer)3.3 客户端事务处理类(DemoUdpNettyClientHandler)3.4 客户端连接类(DemoUdpNettyClient) 4 高并发性能配置5 推荐参考资料6 Github 源码 1 摘…

ROLL.DBF回滚表空间增长问题(达梦数据库)

达梦数据库 - 回滚表空间增长问题 环境介绍1 环境搭建1.1 创建表与测试数据1.2 查询待提交的数据量1.3 查询回滚表空间使用情况1.3.1 插入数据前查询结果1.3.2 插入数据后未提交事务查询结果1.3.3 插入数据后提交事务查询结果 环境介绍 达梦数据库ROLL.DBF 在某些业务系统厂商…

防破解暗桩思路:检查菜单是否被非法修改过源码

本篇文章属于《518抽奖软件开发日志》系列文章的一部分。 我在开发《518抽奖软件》(www.518cj.net)的时候,为了防止被破解,需用添加一些暗桩,在合适的时机检查软件是否被非法修改过,如果被非法修改就做出提…

【位运算】位运算常用技巧总结

目录 前言 一.常见的小问题 1.给定一个数n,确定它的二进制表示中的第x位是0还是1 2.给定一个数n,将它的二进制表示中的第x位修改成1 3.给定一个数n,将它的二进制表示中的第x位修改成0 4.给定一个数n,提取它的二进制表示中最右侧的1&…

AUTOSAR开发工具DaVinci Configurator里的Modules

DaVinci Configurator 里面有个Module这个概念。 如你所想,基本上跟AUTOSAR架构里面的Module相对应 从软件的Project菜单中的Basic Editor项可以打开 打开这个菜单后,会看到很多Modules项以及其相关配置项 这个Basic Editor显示出整个ECU配置中的所有…

Windows 安装 RabbitMq

Windows 上安装 RabbitMQ 的步骤 RabbitMQ 是一个强大的开源消息队列系统,广泛用于构建分布式、可扩展的应用程序。本教程将带您一步一步完成在 Windows 系统上安装 RabbitMQ 的过程。无需担心,即使您是初学者,也能够轻松跟随这些简单的步骤…

element-plus 设置 el-date-picker 弹出框位置

前言 概述:el-date-picker 组件会自动根据空间范围进行选择比较好的弹出位置,但特定情况下,它自动计算出的弹出位置并不符合我们的实际需求,故需要我们手动设置。 存在的问题:element-plus 中 el-date-picker 文档中并…

渗透测试漏洞原理之---【任意文件包含漏洞】

文章目录 1、文件包含概述1.1 文件包含语句1.1.1、相关配置 1.2、动态包含1.2.1、示例代码1.2.2、本地文件包含1.2.3、远程文件包含 1.3、漏洞原理1.3.1、特点 2、文件包含攻防2.1、利用方法2.1.1、包含图片木马2.1.2、读取敏感文件2.1.3、读取PHP文件源码2.1.4、执行PHP命令2.…