【Redis】基于Redis实现秒杀功能
业务的流程大概就是,先判断优惠卷是否过期,然后判断是否有库存,最好进行扣减库存,加入全局唯一id,然后生成订单。

一、超卖问题
真是的场景下可能会有超卖问题,比如开200个线程进行抢购,抢100个商品,最后发现生产力109个订单,库存发现是-9,这就出现了超卖问题。
这个是怎么出现的呢?比如我现在判断库存为1那么我开始扣减库存,此时还没扣减的时候,线程二来,发现库存还是大于0,那么我线程2也开始扣减库存,此时线程一和线程二都执行扣减,就导致库存从1变为-1

有两种解决方案,悲观锁和乐观锁,悲观锁就是用lock 或者 synchronized,让所有线程变成串行方式执行,乐观锁是判断之前查到的数据是否被修改了,如果被修改了就不允许下一步操作,重新获取最新的数据进行操作。
乐观锁:版本号法设置版本号,每次进行减库存的时候都要进行版本+1。大概流程是这样的,先进行查询,版本号=1,库存=1,然后线程二进来了同样查询库存=1,版本号=1,然后线程1开始判断版本号是否等于之前查出来的版本号1,如果相等就更新并且版本+1。然后线程二在进行判断此时判断版本号与自己线程当前查出的版本号不一致1≠2了,此时更新失败。

简化玩法,通过数据业务本身进行判断原本数据是否有变化,例如查出库存然后扣减库存的时候如果发现库存与查出的库存不一致,说明期间有线程将库存修改,那么就修改失败。
二、一人一单问题
同一个优惠卷,一个只能抢一次。在多线程情况下可能会出现一个人强好几次,都抢到了的情况。这和之前超卖问题差不多,都是第一次检查自己有没有抢购这个优惠卷的时候判断认为自己没有抢过,此时其他线程进来也查数据库也没有,所以会同时新增优惠卷抢购订单。但由于之前的超卖是修改的问题,而这个是新增的问题,所以不太好用乐观锁。
可以用悲观锁,先获取用户的id,根据用户id获取锁提交事务然后释放锁,因为不同用户可以同时操作 ,但是同一个用户只能串行执行避免并发问题。

三、集群模式下一人一单问题
对于多集群下,服务器有多个,可能用户会访问不同的服务器,假如在抢优惠卷的时候,分别发送了两个请求访问,然后分别发送请求到两台服务器,那么每台服务器的Tomcat不同,jvm也就不同,那么他们获取的锁对象也是不同的,所以同一个用户在这种情况下用synchronized是锁不住的。

解决方案使用分布式锁
分布式锁
必须满足在多集群多线程下,多进程可见,并且互斥。mysql性能一般,安全性可以,高可用还可以,可以理由x锁锁住某条数据进行作为全局锁,然后通过报异常回滚释放锁。然后用redis的话,效率高,高可用,可以拓展主从机制,使用SETNX完成互斥。

使用redis方式来实现分布式锁。

代码实现

setIfAbsent就相当于NX,然后时间是EX设置超时时间目的是为了宕机或者卡主,锁不释放的情况。
锁的误删问题
线程1在业务过程中卡住了,对应的锁因为时间太长锁失效了删除了,此时业务二进来获取锁,那么获取成功,在正常业务过程中业务一恢复正常,然后快结束的时候将锁释放,此时会把业务二中拿到的锁给释放掉。

解决方法就是在获取锁的时候要存入线程id,释放锁的时候判断锁的线程id是不是自己的,是自己的才能释放,确保锁不会误删。
但是极端的情况下还会有锁的误删问题,比如在业务获取锁执行完毕之后,在进行判断是否是自己当前线程的锁,如果是那么此时突然阻塞,等下一个线程进行业务过程中获取锁,然后执行一般的时候线程一恢复正常他会进行释放锁,因为在阻塞之前进行过判断是否是当前线程,此时只执行释放锁的操作,那么依然会将就锁删除掉。
解决方法
要保证 判断是否是当前锁已经释放锁的过程是原子性的,要一块进行操作。我们可以用lua脚本,在里面执行redis的操作要么都执行成功要么都失败。
在以上这些锁有一个问题,就是不可重入,不可重试,超时释放,主从一致性(用主节点加了一个锁,但是主从未同步完成的时候主节点挂了,那么此时其他线程又要获取锁发现从节点没有锁标志那么就会出现同一把锁获取两次不同线程的问题)。
使用Redisson解决
Redisson实现可重入锁原理
是一个hash结构,key是锁的名字,对应的键值对的key是锁的线程的id,value是锁的使用次数,如果不存在就正常加锁,默认是value的value是1,所以如果解锁就将这个值-1,判断为0就释放锁,如果是重入的话,会判断这个锁是不是当前线程的如果是的话就会将锁的value的value+1,直到将锁逐层释放等到value的value为0时才释放。这些操作实际上是写在lua脚本里,保证原子性。

Redisson实现锁的可重试,超时效
首先会获取锁,判断锁是否存在,如果不存在就获取成功,如果手动设置了超时时间就直接结束。如果没有设置超时时间,看门狗会一直重复续约超时时间,默认是30秒然后30/3每隔10秒续约10秒,一直往目的是防止业务还没完成就自动释放锁。然后当时判断锁存在,那么会判断锁的时间还有吗,没有的话直接结束,如果有的话会有信号量机制订阅信息,等待锁的释放,如果收到锁释放的信息,那么它会再次判断是否超时,如果超时了结束,如果没超时重新获取一遍锁。如果锁释放成功会取消看门狗,因为业务结束会释放锁,所以意味着业务结束。
Redisson实现锁的主从一致性。
它实现的方法就是比如有三个节点,每次加锁必须将三个节点都加上锁,才叫获取锁成功,以此为依据。假设这三个节点有对应的从节点,假设其中有一个主节点崩溃,从节点作为主节点,此时如果主从没有及时更新,那么从节点作为主节点发现没有锁的表示,而其他两个阶段是正常的有锁的表示,此时如果有个线程趁虚而入,想获取锁此时只有第一个节点能获取锁,其他两个节点由于之前加了锁了,所以不能获取到锁,所以加锁失败。

相关文章:
【Redis】基于Redis实现秒杀功能
业务的流程大概就是,先判断优惠卷是否过期,然后判断是否有库存,最好进行扣减库存,加入全局唯一id,然后生成订单。 一、超卖问题 真是的场景下可能会有超卖问题,比如开200个线程进行抢购,抢100个…...
Hadoop 使用过程中 15 个常见问题的详细描述、解决方案
目录 问题 1:配置文件路径错误问题描述解决方案Python 实现 问题 2:YARN 资源配置不足问题描述解决方案Python 实现 问题 3:DataNode 无法启动问题描述解决方案Python 实现 问题 4:NameNode 格式化失败问题描述解决方案Python 实现…...
【Flutter 问题系列第 84 篇】如何清除指定网络图片的缓存
这是【Flutter 问题系列第 84 篇】,如果觉得有用的话,欢迎关注专栏。 博文当前所用 Flutter SDK:3.24.3、Dart SDK:3.5.3,网络图片缓存用的插件 cached_network_image: 3.4.1,缓存的网络图像的存储和检索用…...
【UE5】使用基元数据对材质传参,从而避免新建材质实例
在项目中,经常会遇到这样的需求:多个模型(例如 100 个)使用相同的材质,但每个模型需要不同的参数设置,比如不同的颜色或随机种子等。 在这种情况下,创建 100 个实例材质不是最佳选择。正确的做…...
鸿蒙动画开发07——粒子动画
1、概 述 粒子动画是在一定范围内随机生成的大量粒子产生运动而组成的动画。 动画元素是一个个粒子,这些粒子可以是圆点、图片。我们可以通过对粒子在颜色、透明度、大小、速度、加速度、自旋角度等维度变化做动画,来营造一种氛围感,比如下…...
IDEA2023 创建SpringBoot项目(一)
一、Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。 二、快速开发 1.打开IDEA选择 File->New->Project 2、…...
VSCode:终端打开一片空白,无cmd
第一步:找到右下角设置图标 第二步:找到 Terminal - Integrated - Default Profile: Windows: 选择一个本地存在的命令方式,重启即可 也可以直接在右下角直接选择...
Zea maize GO
1.涉及到新旧基因组的转化 B73v4_to_B73v5 (davidbioinformatics只支持新版基因组) MaizeGDB Map文件下载https://download.maizegdb.org/Pan-genes/B73_gene_xref/小处理脚本(制作map文件) import pandas as pd# 读取CSV文件 …...
Android开发实战班 - 数据持久化 - 数据加密与安全
在 Android 应用开发中,数据安全至关重要,尤其是在处理敏感信息(如用户密码、支付信息、个人隐私数据等)时。数据加密是保护数据安全的重要手段,可以有效防止数据泄露、篡改和未经授权的访问。本章节将介绍 Android 开…...
EDA实验设计-led灯管动态显示;VHDL;Quartus编程
EDA实验设计-led灯管动态显示;VHDL;Quartus编程 引脚配置实现代码RTL引脚展示现象记录效果展示 引脚配置 #------------------GLOBAL--------------------# set_global_assignment -name RESERVE_ALL_UNUSED_PINS "AS INPUT TRI-STATED" set_…...
Eclipse 查找功能深度解析
Eclipse 查找功能深度解析 Eclipse 是一款广受欢迎的集成开发环境(IDE),它为各种编程语言提供了强大的开发工具。在本文中,我们将深入探讨 Eclipse 的查找功能,这是开发者日常工作中不可或缺的一部分。无论是查找代码中的特定字符串,还是进行更复杂的搜索,如正则表达式…...
第三百二十九节 Java网络教程 - Java网络UDP套接字
Java网络教程 - Java网络UDP套接字 TCP套接字是面向连接的,基于流。基于UDP的套接字是无连接的,基于数据报。 使用UDP发送的数据块称为数据报或UDP数据包。每个UDP分组具有数据,目的地IP地址和目的地端口号。 无连接套接字在通信之前不建立…...
Leetcode215. 数组中的第K个最大元素(HOT100)
链接 第一次: class Solution { public:int findKthLargest(vector<int>& nums, int k) {sort(nums.begin(),nums.end());int n nums.size();return nums[n-k];} }; 这显然不能出现在面试中,因为面试官考察的不是这个。 正确的代码&#…...
QT与嵌入式——搭建串口
1、源码 由于我需要不止一个串口来进行数据交互,所以简单的封装了一下 void Usb_Init(QString portName, QSerialPort *Port) {Port->setPortName(portName);Port->setBaudRate(QSerialPort::Baud115200); // 设置波特率,根据你的开发板配置修改…...
Shell编程-6
声明:学习视频来自b站up主 泷羽sec,如涉及侵权马上删除文章 感谢泷羽sec 团队的教学 视频地址:shell(6)if条件判断与for循环结构_哔哩哔哩_bilibili 一、if条件判断 在Shell脚本中,if语句用于基于条件的评估来执行不同的代码块。…...
使用 Postman 设置 Bearer Token 进行身份验证
学习笔记 1. 打开 Postman 并创建新请求 打开 Postman。 在左上角点击 按钮,创建一个新的请求。 2. 选择 HTTP 方法 在请求类型(默认为 GET)旁边的下拉菜单中,选择你需要的 HTTP 方法,如 POST、GET、PUT 等。 3…...
现在转前端怎么样?
互联网技术日新月异,软件开发者追逐技术浪潮的脚步从未停歇。在这个快速发展的行业中,如何规划自己的职业道路,选择合适的技术方向,成为了许多开发者面临的重要抉择。本文将围绕技术选择这个话题,分享一些深入的思考和…...
【算法一周目】滑动窗口(1)
目录 长度最小的子数组 解题思路 代码实现 无重复字符的最大字串 解题思路 代码实现 最大连续1的个数l l l 解题思路 代码实现 将x减到0的最小操作数 解题思路 代码实现 长度最小的子数组 题目链接:209. 长度最小的子数组题目描述: 给定一个…...
React Native 基础
React 的核心概念 定义函数式组件 import组件 要定义一个Cat组件,第一步要使用 import 语句来引入React以及React Native的 Text 组件: import React from react; import { Text } from react-native; 定义函数作为组件 const CatApp = () => {}; 渲染Text组件...
【C++笔记】list使用详解及模拟实现
前言 各位读者朋友们大家好!上期我们讲了vector的使用以及底层的模拟实现,这期我们来讲list。 目录 前言一. list的介绍及使用1.1 list的介绍1.2 list的使用1.2.1 list的构造1.2.2 list iterator的使用1.2.3 list capacity1.2.4 list element access1.…...
Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...
html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...
JDK 17 序列化是怎么回事
如何序列化?其实很简单,就是根据每个类型,用工厂类调用。逐个完成。 没什么漂亮的代码,只有有效、稳定的代码。 代码中调用toJson toJson 代码 mapper.writeValueAsString ObjectMapper DefaultSerializerProvider 一堆实…...
