Redis - 优惠卷秒杀

场景分析

为了避免对数据库造成压力,我们在新增优惠卷的时候,可以将优惠卷的信息储存在Redis中,这样用户抢购的时候访问优惠卷信息,通过Redis读取信息。
在这里插入图片描述
抢购流程
在这里插入图片描述

业务分析

既然在新增优惠卷的时候,我们只需要id和库存信息,那么这个时候我们需要说选择Redis中Stringt数据类型,以id作为键,此时我们在进行抢劵的时候,通过id可以直接查询到对应的库存信息,进行判断。
此时我们会面临一下场景:

  1. 出现超卖问题
  2. 不能保证一人一单

超卖问题

锁结构选型:

在这里插入图片描述
读多写少的场景:
选择乐观锁。因为读操作不会加锁,可以提高系统的并发性能。写操作通过版本号或条件更新来避免冲突。
写操作频繁且并发量高的场景
选择悲观锁。悲观锁通过加锁机制可以有效避免并发冲突,保证数据的一致性和正确性。
性能和一致性权衡:
在性能和一致性之间需要权衡。如果一致性要求非常高,且系统能够接受较低的并发性能,选择悲观锁。如果系统需要高性能,并且能够接受偶尔的冲突重试,选择乐观锁。
由于这个优惠卷抢票属于,读多写少这种场景,那么我们选型的话选择乐观锁会比较合适。

数据库乐观锁解决超卖问题
超卖问题分析

在这里插入图片描述
假设我们现在库存store = 1 ,那么此时线程一 判断之后 store > 0 那么这个时候线程一开始进行库存缩减,但是此时库存还没开始在数据库中扣减,此时线程二开始查询,发现 store > 0,那么线程二也认定自己抢票成功,开始扣减库存,那么此时就会出现store 减少了两次,那么库存就变成了-1,假设在线程二查询之后,线程一库存扣减之前,线程三又进行了判断,那么此时库存就又变成了-2。这是超卖问题的一个场景。
乐观锁,版本号法:
在这里插入图片描述
版本号法:也就是说引入一个新的字段用来检测当前我查询到的version,和我修改时的version是否是同一个,这样的话保证不会出现超卖问题,但是同样,会出现大量失败情况导致错误率极高。不推荐使用
CAS方案:
比较我查询到的库存值跟我更新后的库存值判断。但是这种情况也会出现上述的那种限制,所以我们只要判断扣减前的库存是否大于0即可,那么这种情况效率高,且不会出现大幅度失败。这个主要还是依靠mysql自身的行级锁机制进行的。属于悲观锁的一种范畴。
MySQL 中的行级锁可以确保同一行的数据在同一时间只能被一个事务修改,从而避免了并发更新导致的数据不一致问题。在代码中,当多个线程同时尝试更新同一行数据时,MySQL 会自动对该行数据加锁,以确保只有一个线程能够成功执行更新操作,其他线程会被阻塞直到锁释放。
这种方案解决较上一种方案会好一点,但依旧不是好的解决方案,因为对于这种场景下,阻塞依旧是不可避免的。

 boolean voucherId1 = iSeckillVoucherService.update().setSql("stock = stock - 1").eq("voucher_id", voucherId).gt("stock", 0).update();

一人一单问题

我们只需要用户下单之后,把用户订单信息,存入Redis中,只需要到时候读取这个信息判断有多少次下单即可。

// 这个位置采用优惠卷id 和 用户 id作为键,储存对应的信息,避免抢其他票时出现误判
String keyUserOrder = "user:order:" + voucherId + user.getId();
Integer count = 0;
try{count  = Integer.valueOf(stringRedisTemplate.opsForValue().get(keyUserOrder));if (count >= 1) {return Result.fail("用户已购买");}
}catch (Exception e){log.error("第一次抢票");
}
// 这个是抢票成功时,将订单信息保存。值自增用来判断是否是第一次购买
Long orderCount = stringRedisTemplate.opsForValue().increment(keyUserOrder, 1);
新的实现思路

既然是抢劵,那我确实无法避免这个内容。我可以说实现一个优惠卷池,将这个内容存入Redis中,抢到的话就生成订单,这样的话也没有超卖问题。只不过还得解决一人一单。效率较上面这种会更好一点。

实现步骤
  • 初始化优惠券池:将每张优惠券的信息预先存入Redis。
  • 抢券逻辑:用户请求时,从Redis中获取一张优惠券。
  • 生成订单:抢到优惠券后,生成订单并将订单信息和优惠券信息关联起来。
  • 一人一单检查:确保同一用户不能重复抢券。
代码示例

以下是一个简单的代码实现示例:
初始化优惠券池:

// 初始化优惠券池,将100张优惠券信息存入Redis
String voucherPoolKey = "voucher:pool";
for (int i = 1; i <= 100; i++) {// 假设优惠券信息是一个简单的字符串,这里可以是更复杂的对象stringRedisTemplate.opsForList().leftPush(voucherPoolKey, "voucher_" + i);
}

抢券逻辑

public Result grabVoucher(Long voucherId, Long userId) {// Redis中的键String voucherPoolKey = "voucher:pool";String userOrderKey = "user:order:" + voucherId + ":" + userId;// 检查用户是否已经抢过if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(userOrderKey))) {return Result.fail("用户已购买");}// 从Redis中弹出一张优惠券String voucher = stringRedisTemplate.opsForList().rightPop(voucherPoolKey);if (voucher == null) {return Result.fail("优惠券已抢完");}// 抢券成功,生成订单// 这里假设订单生成成功,实际应用中需要加入订单生成逻辑String orderInfo = "order_for_" + voucher + "_by_user_" + userId;// 记录用户抢券信息stringRedisTemplate.opsForValue().set(userOrderKey, orderInfo);return Result.ok("抢券成功", orderInfo);
}

订单生成
实际的订单生成过程可能涉及复杂的业务逻辑和数据库操作,这里简化为成功后记录用户抢券信息。

一人一单的实现
在用户抢券前,检查用户是否已经抢过,通过Redis键的存在性判断。

// 检查用户是否已经抢过
if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(userOrderKey))) {return Result.fail("用户已购买");
}
优点
  • 高效处理并发:Redis的高吞吐量和低延迟使得这种方案在高并发场景下表现出色。
  • 避免超卖:优惠券从Redis中弹出后即减少,确保不会超卖。
  • 简单实现一人一单:通过Redis键值对存储和检查用户抢券信息,轻松实现一人一单的限制。
  • 简化订单生成:在抢券成功后,立即生成订单并将信息存储在Redis中,减少了数据库操作的复杂性。

或者我感觉这个并发场景比较大的情况下,使用RabbitMQ削峰填谷,做限流也可以。

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

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

相关文章

【c语言】了解指针,爱上指针(5)

了解指针&#xff0c;爱上指针&#xff08;5&#xff09; 回调函数qsort函数冒泡排序模拟实现qsort函数 回调函数 回调函数&#xff1a;就是一个通过函数指针调用的函数。 把函数的指针作为参数传给另一个函数&#xff0c;当这个指针被用来调用指向的函数时&#xff0c;此时被…

驱动编译报error: negative width in bit-field ‘<anonymous>’错误

错误如下图所示&#xff1a; 代码如下&#xff1a; 问题点&#xff1a;module_param的其他用户的权限参数上。 在Linux中&#xff0c;文件权限由读(r)、写(w)、执行(x)权限组成&#xff0c;分别对应数值4、2、1。 第一位0是占位符&#xff0c;在这里没有意义&#xff0c;因为…

如何使用DotNet-MetaData识别.NET恶意软件源码文件元数据

关于DotNet-MetaData DotNet-MetaData是一款针对.NET恶意软件的安全分析工具&#xff0c;该工具专为蓝队研究人员设计&#xff0c;可以帮助广大研究人员轻松识别.NET恶意软件二进制源代码文件中的元数据。 工具架构 当前版本的DotNet-MetaData主要由以下两个部分组成&#xf…

java图书电子商务网站的设计与实现源码(springboot+vue+mysql)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的图书电子商务网站的设计与实现。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 图书电子商…

基于附带Attention机制的seq2seq模型架构实现英译法的案例

模型架构 先上图 我们这里选用GRU来实现该任务&#xff0c;因此上图的十个方框框都是GRU块&#xff0c;如第二张图&#xff0c;放第一张图主要是强调编码器的输出是作用在解码器每一次输入的观点&#xff0c;具体的详细流程图将在代码实现部分给出。 编码阶段 1. 准备工作…

【C++进阶】AVL树

0.前言 前面我们已经学习过二叉搜索树了&#xff0c;但如果我们是用二叉搜索树来封装map和set等关联式容器是有缺陷的&#xff0c;很可能会退化为单分支的情况&#xff0c;那样效率就极低了&#xff0c;那么有没有方法来弥补二叉搜索树的缺陷呢&#xff1f; 那么AVL树就出现了&…

6.小程序页面布局 - 账单明细

文章目录 1. 6.小程序页面布局 - 账单明细1.1. 竞品1.2. 布局分析1.3. 布局demo1.4. 页面实现-头部1.5. 账单明细1.5.1. 账单明细-竞品分析1.5.2. 账单明细-实现1.5.2.1. 账单明细-实现-mock数据1.5.2.2. 每日收支数据的聚合整理1.5.2.3. 页面scroll-view 1.6. TODO 1. 6.小程序…

java —— 封装、继承、接口和多态

一、封装 封装是将数据和操作这些数据的方法整合成一个类。在这个类中&#xff0c;用 private 修饰符将某些数据隐藏起来&#xff0c;只通过特定的方法实现这些数据的访问和修改&#xff0c;以此实现数据的完整和安全性。 封装的步骤&#xff1a; 二、继承 继承是指把子类共有…

Meta发布Chameleon模型预览,挑战多模态AI前沿

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

【数据结构与算法】之堆的应用——堆排序及Top_K问题!

目录 1、堆排序 2、Top_K问题 3、完结散花 个人主页&#xff1a;秋风起&#xff0c;再归来~ 数据结构与算法 个人格言&#xff1a;悟已往之不谏&#xff0c;知来者犹可追 克心守己&#xff0c;律己则安&#xff01; 1、堆排序 对一个无序的数组…

03_前端三大件CSS

文章目录 CSS用于页面元素美化1.CSS引入1.1style方式1.2写入head中&#xff0c;通过写style然后进行标签选择器加载样式1.3外部样式表 2.CSS样式选择器2.1 元素选择器2.2 id选择器2.3 class选择器 3.CSS布局相关3.1 CSS浮动背景&#xff1a;先设计一些盒子因此&#xff0c;引出…

[图解]产品经理创新模式02改善信息流转

1 00:00:02,160 --> 00:00:04,000 第二种改进模式 2 00:00:04,010 --> 00:00:06,340 就是改善信息流转 3 00:00:06,550 --> 00:00:08,000 它是这样的 4 00:00:09,250 --> 00:00:11,290 当电脑系统越来越多的时候 5 00:00:11,300 --> 00:00:12,530 就会出现这…

linux centos stream 9 定时任务

定时任务,也称为计划任务,指在规定时间执行某项任务。在各操作系统中都有此功能,如Windows下的计划任务:定时关机等。 linux用户定时任务和系统定时任务是在Linux操作系统中用于自动执行特定任务的机制。它们基于cron(cron daemon)服务来完成的。 cron是linux系统中以后台…

Hive运行错误

Hive 文章目录 Hive错误日志错误SessionHiveMetaStoreClientql.Driver: FAILED: Execution Error, return code 2 from org.apache.hadoop.hive.ql.exec.mr.MapRedTaskerror: Could not find or load main class org.apache.hadoop.mapreduce.v2.app.MRAppMaster Please check …

DOS学习-目录与文件应用操作经典案例-type

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一.前言 二.使用 三.案例 1. 查看文本文件内容 2. 同时查看多个文本文件内容 3. 合并文…

可视化 | Seaborn中的矩阵图及示例

Seaborn是python提供的一个很棒的可视化库。它有几种类型的绘图&#xff0c;通过这些绘图&#xff0c;它提供了惊人的可视化能力。其中一些包括计数图&#xff0c;散点图&#xff0c;配对图&#xff0c;回归图&#xff0c;矩阵图等等。本文讨论了Seaborn中的矩阵图。 示例1&am…

第十三期Big Demo Day聚焦Web3前沿,FaceN.AI项目路演揭幕创新技术

第十三期Big Demo Day活动即将于2024年5月28日在香港数码港的CyberArena隆重举行。FaceN.AI将亮相本次Big Demo Day&#xff0c;参与精彩的项目路演&#xff0c;展示其在跨链去中心化数字身份、On-chain to Off-chain数据应用、DIDFi探索以及元宇宙与AIGC人格化发展等领域的领先…

HTML静态网页成品作业(HTML+CSS)——宠物狗介绍网页(3个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有3个页面。 二、作品演示 三、代…

下载CentOS系统或者下载Ubuntu系统去哪下?

因为Centos官网是挂在国外的服务器上&#xff0c;下载镜像时相比于国内的下载速度会慢很多&#xff0c;分享国内的镜像站去阿里巴巴下载Centos镜像。 首先分享两种下载方式&#xff0c;如果只想下载Centos那么就访问方式一的下载地址即可&#xff0c;如果还想下载其他的系统&a…

【算法设计与分析】基于Go语言实现动态规划法解决TSP问题

本文针对于最近正在学习的Go语言&#xff0c;以及算法课实验所需内容进行Coding&#xff0c;一举两得&#xff01; 一、前言 由于这个实验不要求向之前的实验一样做到那种连线的可视化&#xff0c;故可以用图形界面不那么好实现的语言进行编写&#xff0c;考虑到Go语言的…