当前位置: 首页 > news >正文

【Redis】基于Redission实现分布式锁(代码实现)

目录

基于Redission实现分布式锁解决商品秒杀超卖的场景:

1.引入依赖:

2.加上redis的配置:

3.添加配置类:

4.编写代码实现:

5.模拟服务器分布式集群的情况:

        1.右键点击Copy Configuration

        2.点击Modify option

        3. 选择VM option(用于指定新的端口)

        4.输入想要指定的端口(比如):-Dserver.port=8082 点击Apply

        5.出现新的进程,点击启动,就可以进行分布式多节点测试。

使用Jmeter进行压测


        (单机部署)多线程高并发情况下对同一个共享资源进行读写时,会出现数据错乱(数据不一致)的问题;加锁(同步锁)可以解决出现数据不一致的问题;(其他线程进行等待持有锁的线程执行完成后才能进行正常的处理)。
        但是随着用户量日益增多,单个服务器压力越来越大,所以使用多个服务器进行分布式集群部署,虽然降低了服务器的压力,提高了服务器的吞吐量。但是还是会出现数据不一致的问题,这时候发现是是同步锁(synchronized)的问题,同步锁基于JVM的,他只能锁住单个服务器中一个线程,但是经过分布式集群部署过后,每台服务器在并发的情况下只能锁住一个线程,所以高并发情况下,还是会出现数据错乱的情况。(假如4个服务器,每台服务器都处于高并发的情况,然后同步锁只能锁住一个线程,这时候每个服务器锁住一个线程,最多可以出现4个线程同时对数据库进行操作,就会造成数据不一致的问题(例如常说的秒杀超卖的情况))

        经过查阅资料发现可以通过分布式锁可以解决,然后有三种主流的分布式锁的解决方案分别是使用基于Mysql/Zookeeper/redis实现分布式锁。由于我们系统使用到了Redis,考虑Zookeeper需要重新部署到服务器,避免间接增加服务器的成本,所以直接使用Redis来实现分布式锁。我们发现使用Redis的setNX可以很简单的实现分布式锁。
        那么setNX的特性是什么呢?
        当一个线程进来,往Redis当中通过setNX去存储一个值的时候,他会根据键值(key)去查看是否存在value值,没有就存储一个值返回true,有就返回一个false,注意一定要加上锁的过期时间,避免线程阻塞。(当用户在请求的过程当中,通过setNX进行加锁完成的时候,这个服务器挂掉了,当其他线程进行setNX进行上锁的时候,发现键当中一直会有值,造成了死锁,其他线程加锁不成功就会造成阻塞)。所以一定要加上过期时间。

        随着业务的扩展,又出现了一些问题,就是1.业务的处理时间超过了锁的过期时间和2.线程1可能释放了线程2所持有的锁,【线程1还没将业务处理完成就释放锁,导致线程2拿到锁处理自己的业务。当线程1执行完成后,释放了锁,但是此时线程2已经拿到了当前锁,所以线程1释放的是线程2的锁。】
        如何解决这些问题呢?
        1.加长锁的过期时间,并增加子线程每10秒去确认线程是否在线。在线则将过期时间重设(续命--他们所说的看门狗);
        2.给锁的值设置一个唯一ID(UUID)-(使用setNx进行尝试获取锁的时候,如果获取成功,将锁的值设置为一个唯一的ID,释放锁的时候会拿着key去获取锁的值是否与自己的唯一ID一致,一致才进行释放锁,从而就不会释放其他线程的锁)

        上述说这么多,如果让我们自己写起来确实有些麻烦,这时候查阅发现redis他本身已经提供了一个Redission的组件已经解决了这些问题。

那么下面我就提供一个

基于Redission实现分布式锁解决商品秒杀超卖的场景:

springboot版本:2.6.13

redission版本:3.22.0

1.引入依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.22.0</version></dependency>

2.加上redis的配置:

server:port: 8083
spring:redis:host: 127.0.0.1password:port: 6379timeout: 1000

3.添加配置类:

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RedissonConfig {@Value("${spring.redis.host}")private String host;@Value("${spring.redis.port}")private String port;@Beanpublic RedissonClient getRedisson(){Config config = new Config();//单机模式  依次设置redis地址和密码config.useSingleServer().setAddress("redis://" + host + ":" + port).setTimeout(30000); // 设置缓存过期时间为30秒return Redisson.create(config);}
}

4.编写代码实现:


import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import java.util.Objects;@RestController
@RequestMapping("/redisLock")
public class RedisLockController {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Resourceprivate RedissonClient redisson;// 秒杀商品keyprivate static final String REDIS_KEY = "secKillProductKey:";private static final String LOCK_KEY = "secKillProduct";// 秒杀商品总个数private static final int PRODUCT_SIZE = 1000;/*** 初始化秒杀商品总个数到redis中* 注意:测试的时候,先调用这个接口初始化库存*/@GetMapping("/init")public void init() {// 初始化库存 将库存存到redis中stringRedisTemplate.opsForValue().set(REDIS_KEY, String.valueOf(PRODUCT_SIZE));}/*** 秒杀*/@GetMapping("/secKill")public void secKill() {// 获取锁RLock lock = redisson.getLock(LOCK_KEY);try {// 加锁lock.lock();int s = Integer.parseInt(Objects.requireNonNull(stringRedisTemplate.opsForValue().get(REDIS_KEY)));if (s > 0) {// 扣库存s--;System.out.printf("秒杀商品个数剩余:" + s + "\n");// 更新库存stringRedisTemplate.opsForValue().set(REDIS_KEY, String.valueOf(s));} else {System.out.println("活动太火爆了,商品已经被抢购一空了!");}} catch (Exception e) {System.out.println(Thread.currentThread().getName() + "异常:");e.printStackTrace();} finally {// 释放锁lock.unlock();}}
}

5.模拟服务器分布式集群的情况:

        同一个服务不同端口,同时运行两个相同的主程序。

在service中复制一个进程,指定不同端口

        1.右键点击Copy Configuration

        2.点击Modify option

        3. 选择VM option(用于指定新的端口)

        4.输入想要指定的端口(比如):-Dserver.port=8082 点击Apply

        5.出现新的进程,点击启动,就可以进行分布式多节点测试。

使用Jmeter进行压测

首先需要先调用一下初始化的接口:127.0.0.1:8082/redisLock/init或127.0.0.1:8083/redisLock/init

1.设置线程组

2.两个HTTP请求,请求不同的端口(8082、8083)进行测试高并发多线程的情况:

3.服务器打印结果

经过测试,一秒钟1000个线程同时请求秒杀接收并没有出现超卖的问题。  

拓展:

       Redis本身是一个CP(一致性和分区容错性)模式的数据库,它通过主从复制实现高可用性,当主节点挂掉时,从节点会自动进行选举,选出一个新的主节点继续提供服务。但是,在主节点挂掉之前,它可能还来不及将最新的数据同步到从节点,这时就会出现数据不一致的问题。

         如果redis采用主从模式进行部署,当往redis中通过setNX进行加锁的过程中,主节点挂了,主节点的数据并没有同步到从节点当中,这种怎么办?

        可以使用RedLock(红锁)解决,RedLock是一个分布式锁的实现,它可以通过访问多个Redis节点来实现更高的可用性和一致性。RedLock的工作原理是,在加锁时,向多个Redis节点发送请求,只有当所有节点都成功返回时,才认为加锁成功。

相关文章:

【Redis】基于Redission实现分布式锁(代码实现)

目录 基于Redission实现分布式锁解决商品秒杀超卖的场景&#xff1a; 1.引入依赖&#xff1a; 2.加上redis的配置&#xff1a; 3.添加配置类&#xff1a; 4.编写代码实现&#xff1a; 5.模拟服务器分布式集群的情况&#xff1a; 1.右键点击Copy Configuration 2.点击Modi…...

websocket 安全通信

WebSocket 协议 WebSocket&#xff1a;在 2008 年诞生&#xff0c;2011 年成为国际标准。它允许服务器主动向客户端推送信息&#xff0c;客户端也可以主动向服务器发送信息&#xff0c;实现了真正的双向平等对话。它是一种在单个 TCP 连接上进行全双工通讯的协议&#xff0c;能…...

代码生成技术技术-janino

文章目录 背景简单例子flink 例子 背景 代码生成技术适合在需要动态代码编译的场景中使用。比如大数据计算场景下&#xff0c;经常会要把flink sql 转成实际的执行计划 简单例子 代码是一个string 类型&#xff0c;直接用janino 编译后&#xff0c;就可以得到加载到jvm里的c…...

QT事件处理系统之四:自定义事件的注册及拦截、发送

1、自定义事件的注册 自定义事件对象 自定义的事件类必须继承自QEvent,并且无需指定父类 自定义的事件类必须拥有全局唯一的Type值,使用QEvent::User+value即可。 class MyEvent : public QEvent {public:MyEvent();QString getStr() {...

vs2022 studio控制台出现中文乱码解决

vs2022 studio控制台出现中文乱码解决 问题解决 问题 这里cout中间的中文&#xff0c;但控制台出现的是乱码对此需要进行修改 解决 打开运行的主文件&#xff0c;也就是整个程序的入口&#xff0c;对他另存为 之后点击编码保存 接着将编码保存的格式变为图片对应的这种 记…...

支持向量机介绍

一、引言 1.支持向量机&#xff08;SVM&#xff09;的概念和背景 支持向量机&#xff08;Support Vector Machine&#xff0c;简称SVM&#xff09;是一种起源于统计学习理论的分类和回归算法。20世纪60年代&#xff0c;Vapnik等人提出了支持向量机理论&#xff0c;该理论基…...

电压互感器在线监测的原理

电压互感器在线监测的原理主要基于电磁感应、电场效应以及一系列先进的监测技术。以下是对其原理的详细解释&#xff1a; 一、电磁感应原理 电压互感器&#xff08;Voltage Transformer&#xff0c;简称VT&#xff09;本质上是一种降压变压器&#xff0c;它利用电磁感应的原理…...

算法训练与程序竞赛题目集合(L4)

目录 L4-103 就不告诉你 输入格式&#xff1a; 输出格式&#xff1a; 输入样例&#xff1a; 输出样例&#xff1a; L4-104 Wifi密码 输入格式&#xff1a; 输出格式&#xff1a; 输入样例&#xff1a; 输出样例&#xff1a; L4-105 冠军魔术 输入格式&#xff1a; …...

Selenium进行Web自动化测试

Selenium进行Web自动化测试 SeleniumPython实现Web自动化测试一、环境配置 SeleniumPython实现Web自动化测试 一、环境配置 环境基于win10&#xff08;X64&#xff09; 安装Python&#xff1b;安装PyCham安装chomedriver chomedriver下载地址 可以查看本地chrome软件版本下载…...

模拟算法讲解

模拟算法是一种基于实际情况模拟的算法&#xff0c;通过模拟现实世界中的系统或过程&#xff0c;来研究它们的性质和行为。模拟算法可以用于解决各种问题&#xff0c;包括物理模拟、经济模拟、社会模拟等。 模拟算法的基本步骤包括&#xff1a; 定义问题&#xff1a;明确需要模…...

Leetcode 3196. Maximize Total Cost of Alternating Subarrays

Leetcode 3196. Maximize Total Cost of Alternating Subarrays 1. 解题思路2. 代码实现 题目链接&#xff1a;3196. Maximize Total Cost of Alternating Subarrays 1. 解题思路 这一题就是一个动态规划&#xff0c;只需要考虑每一个元素作为开始和处于序列当中的二元态即可…...

Elasticsearch**Elasticsearch自定义插件开发入门

Elasticsearch作为一个强大的搜索引擎和数据分析工具&#xff0c;其强大的扩展性是其受欢迎的重要原因之一。自定义插件开发入门** Elasticsearch作为一个强大的搜索引擎和数据分析工具&#xff0c;其强大的扩展性是其受欢迎的重要原因之一。通过自定义插件&#xff0c;用户可…...

在Ubuntu中创建Ruby on Rails项目并搭建数据库

新建Rails项目 先安装bundle Ruby gem依赖项工具&#xff1a; sudo apt install bundle 安装Node.js: sudo apt install nodejs 安装npm 包管理器&#xff1a; sudo apt install npm 安装yarn JavaScript包管理工具&#xff1a; sudo apt install yarn 安装webpacker: …...

微信小程序反编译 2024 unveilr.exe

ps&#xff1a;一开始用的反编译工具是wxappUnpacker&#xff0c;后面改为 unveilr.exe 1.先找到小程序安装目录“E:\聊天记录\WeChat Files\Applet”&#xff0c;要反编译小程序的包 文件夹下的名字对应的是小程序ID&#xff0c;如果不确定是哪个&#xff0c;可以删除->打…...

测试测量-DMM直流精度

测试测量-DMM直流精度 最近去面试&#xff0c;发现了自己许多不足&#xff0c;比如我从未考虑过万用表准或者不准&#xff0c;或者万用表有多准&#xff1f; 在过去的实验室中&#xff0c;常用的DMM有KEYSIGHT 34401A以及 KEITHLEY THD2015&#xff0c;就以这两台为例&#x…...

AGV机器人的调度开发分析(2)- 内核中的调度

我们开发AGV的调度系统&#xff0c;最重要的是要实现调度的运行&#xff0c;那么调度要执行哪些任务呢&#xff1f; 先来看看德国开发的开源openTCS中的功能定义&#xff1a; openTCS的控制核心是kernel&#xff0c;目的是提供运输系统/工厂的抽象驾驶模型&#xff0c;管理运…...

HTTP详细总结

概念 HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff0c;规定了浏览器和服务器之间数据传输的规则。 特点 基于TCP协议: 面向连接&#xff0c;安全 TCP是一种面向连接的(建立连接之前是需要经过三次握手)、可靠的、基于字节流的传输层通信协议&#xff0c;在…...

【Android】代码中将 SVG 图像转换颜色

要在代码中将 SVG 图像转换为黑色&#xff0c;你可以使用一个库&#xff0c;例如 AndroidSVG 或 SVG-Android。以下是一个简单的示例代码&#xff0c;展示了如何通过代码方式将 SVG 图像改为黑色&#xff1a; // 导入 AndroidSVG 库 import com.caverock.androidsvg.SVG; impo…...

网络故障排查-TCP标志位

目录 1. SYN&#xff08;Synchronize&#xff09; 2. SYN-ACK&#xff08;Synchronize-Acknowledge&#xff09; 3. FIN&#xff08;Finish&#xff09; 4. RST&#xff08;Reset&#xff09; 故障排除步骤 网络流量分析仪中的TCP标志位&#xff08;SYN、SYN-ACK、FIN、RS…...

[Vue3+Vite+TS] Windows用户设置 VITE_CJS_TRACE=true 标志运行你的脚本来记录堆栈跟踪

Windows用户无法直接在CMD中运行官网提供的命令&#xff1a; VITE_CJS_TRACEtrue vite dev由于 VITE_CJS_TRACE 不是 Vite 配置文件中的标准选项&#xff0c;不能直接写入配置文件进行设置。 但可以使用 Node.js 的 cross-env 包来跨平台设置环境变量. 首先&#xff0c;需要安…...

Prompt 写作提示经验:完整格式和技巧

编写prompt以确保输出格式通常需要明确指定您期望的输出结构和内容要求。以下是一些确保输出格式的步骤和技巧&#xff1a; 明确指定格式&#xff1a;在prompt中明确指出您期望的输出格式。例如&#xff0c;如果您需要一个包含标题、子标题和段落的文章&#xff0c;应在prompt中…...

预训练是什么?

预训练是什么&#xff1f; 图像领域的预训练 在介绍图像领域的预训练之前&#xff0c;我们首先介绍下卷积神经网络&#xff08;CNN&#xff09;&#xff0c;CNN 一般用于图片分类任务&#xff0c;并且CNN 由多个层级结构组成&#xff0c;不同层学到的图像特征也不同&#xff…...

深入解析Linux Bridge:原理、架构、操作与持久化配置

一、引言 在计算机网络中&#xff0c;桥接技术扮演着至关重要的角色&#xff0c;它能够实现不同网络设备之间的数据交换与共享。Linux Bridge作为Linux内核提供的一种网络功能&#xff0c;允许用户通过软件方式将多个网络接口桥接在一起&#xff0c;形成一个透明的二层网络。本…...

使用构建缓存优化 Docker 镜像构建

使用构建缓存优化 Docker 镜像构建 目录 实践构建应用程序额外资源后续步骤 假设一个简单的nodejs程序的 Dockerfile如下&#xff1a; FROM node:20-alpine WORKDIR /app COPY . . RUN yarn install --production CMD ["node", "./src/index.js"]当你运…...

微软搁置水下数据中心项目——项目纳蒂克相比陆地服务器故障更少

“我的团队努力了&#xff0c;并且成功了&#xff0c;”COI负责人诺埃尔沃尔什说。 微软已悄然终止了始于2013年的水下数据中心&#xff08;UDC&#xff09;项目“纳蒂克”。该公司向DatacenterDynamics确认了这一消息&#xff0c;微软云运营与创新部门负责人诺埃尔沃尔什表示…...

国产AI算力训练大模型技术实践

ChatGPT引领AI大模型热潮&#xff0c;国内外模型如雨后春笋&#xff0c;掀起新一轮科技浪潮。然而&#xff0c;国内大模型研发推广亦面临不小挑战。面对机遇与挑战&#xff0c;我们需保持清醒&#xff0c;持续推进技术创新与应用落地。 为应对挑战&#xff0c;我们需从战略高度…...

DS:二叉树的链式存储及遍历

​ 欢迎来到Harper.Lee的学习世界&#xff01; 博主主页传送门&#xff1a;Harper.Lee的博客主页 想要一起进步的uu可以来后台找我哦&#xff01; ​ 一、引入 1.1 二叉树的存储方式 在之前接触到的满二叉树和完全二叉树使用的是数组的存储方式&#xff08;DS&#xff1a;树与…...

C#中File类常见用法总结

前言 我们在开发C#软件的过程中&#xff0c;经常需要和文件打交道&#xff0c;那么File类在C#中是我们使用非常频繁的一个类&#xff0c;本文就是详细介绍File类在C#中的常见用法。 1、判断文件是否存在 string fileName "1.txt";bool isExist File.Exists(fileN…...

CesiumJS【Basic】- #007 绘制直线段以避免地球曲率的影响

文章目录 绘制直线段以避免地球曲率的影响1 目标2 实现绘制直线段以避免地球曲率的影响 1 目标 绘制直线段以避免地球曲率的影响 2 实现 在CesiumJS中,直线的弯曲是由地球曲率引起的,因为地球是一个球体而不是一个平面。因此,如果您要在地球上绘制两点之间的直线,它将会…...

解决文件或文件夹无法删除问题

最近呢&#xff0c;发现很多小伙伴都会遇到一个问题&#xff0c;就是当我要删掉一个文件或者软件的时候&#xff0c;就会弹出[操作无法完成&#xff0c;因为其中的文件夹或文件已在另一个程序中打开] 然后我就翻看网站&#xff0c;很多都是什么去**[任务管理器]修改[资源管理器…...