用setnx实现一个分布式锁

news2024/12/29 9:37:44

用setnx实现一个分布式锁

简介

利用Redis的单线程特性,在多个Redis客户端同时通过SETNX命令尝试获取锁,如果返回1表示获取锁成功,否则表示获取锁失败。

Redis Setnx(SET if Not eXists) 命令在指定的 key 不存在时,为 key 设置指定的值。设置成功,返回 1 。 设置失败,返回 0 。

因为Redis的单线程机制,所以可以保证只会有一个客户端成功获取到锁,而其他客户端则会失败。如果获取锁成功,则设置一个过期时间,防止该客户端挂了之后一直持有该锁。客户端释放锁的时候,需要先判断该锁是否仍然属于该客户端,如果是,则通过DEL命令释放锁。

实现

1.添加pom.xml

<!--        redis相关依赖-->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>2.6.4</version>
</dependency>

2.添加配置文件

redis:
  host: localhost
  port: 6379
  jedis:
    pool:
      max-active: 8 # 连接池最大连接数(使用Jedis时)
      max-wait: -1ms # 连接池最大阻塞等待时间(使用Jedis时)
      max-idle: 8 # 连接池中的最大空闲连接(使用Jedis时)
      min-idle: 0 # 连接池中的最小空闲连接(使用Jedis时)

3.编写redisConfig

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);

        // 使用 StringRedisSerializer 来序列化和反序列化 key 值
        template.setKeySerializer(new StringRedisSerializer());
        // 使用 JdkSerializationRedisSerializer 来序列化和反序列化 value 值
        // 也可以自定义序列化器
        template.setValueSerializer(new GenericToStringSerializer<>(Object.class));

        template.afterPropertiesSet();
        return template;
    }
}

4.编写加锁与解锁

@Slf4j
@Component
public class RedisDistributedLock {


    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private int port;


    private final JedisPool jedisPool;

    public RedisDistributedLock() {
        log.info("host:{},port:{}", host, port);
        JedisPool jedisPool1 = new JedisPool(host, port);
        this.jedisPool = jedisPool1;
    }

    /**
     * 加锁
     * @param lockKey
     * @param requestId
     * @param expireTime
     * @return
     */
    public boolean tryLock(String lockKey, String requestId, int expireTime) {
        try (Jedis jedis = jedisPool.getResource()) {

            String result = getSet(lockKey, requestId, expireTime, jedis);
            return "OK".equals(result);
        }
    }

    private static String getSet(String lockKey, String requestId, int expireTime, Jedis jedis) {
        if (jedis.exists(lockKey)) {
            return  "False";
        }else {
             return jedis.setex(lockKey,expireTime,requestId);
        }
    }

    /**
     * 解锁
     * @param lockKey
     * @param requestId
     * @return
     */
    public boolean unlock(String lockKey, String requestId) {
        try (Jedis jedis = jedisPool.getResource()) {
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
            Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
            return Long.parseLong(result.toString()) == 1L;
        }
    }
}

tryLock方法接收三个参数,分别是锁的键值lockKey、加锁的请求标识requestId和锁的过期时间expireTime。该方法会尝试使用Redis的set命令加锁,如果加锁成功则返回true,否则返回false。其中NX表示只在锁的键不存在时设置锁,PX表示锁的过期时间为expireTime毫秒。

SETNX命令自身是不支持设置超时时间的,一般是结合EXPIRE一起使用,常见用法:

SETNX key value
EXPIRE key 10

或者:

SET key value EX 10 NX

unlock方法接收两个参数,分别是锁的键值lockKey和加锁的请求标识requestId。该方法会执行一个Lua脚本,判断当前锁的值是否等于请求标识requestId,如果是则删除锁并返回true,否则返回false。该方法使用eval命令执行Lua脚本,传入锁的键值和请求标识两个参数,返回值是执行结果。

测试

@Test
public void demo(){
    Order order = new Order();
    order.setId(snowflakeIdWorker.nextId());
    order.setName("测试");
    order.setNum(0L);
    for(int i=0;i<1000;i++){
        if(i%2==0){
            order.setNum(order.getNum()-i);
        }else{
            order.setNum(order.getNum()+i);
        }
        //模拟高并发情况下下发订单
        threadPoolExecutor.execute(new Runnable() {
            @Override
            public void run() {
                log.info("下单中,订单号:{}",order.getId());
                //下单
                orderServiceimpl.update(order);
                log.info("下单完成,订单号:{}",order.getId());
            }
        });
    }
}

测试结果

image-20241004202912844

image-20241004203117580

image-20241004203141919

总结

优点

(1)实现简单:SETNX命令实现简单,易于理解和使用。

(2)性能较高:由于SETNX命令的执行原子性,保证了分布式锁的正确性,而且在Redis中,SETNX命令是单线程执行的,所以性能较高。

缺点

(1)锁无法续期:如果加锁方在加锁后的执行时间较长,而锁的超时时间设置的较短,可能导致锁被误释放。

(2)无法避免死锁:如果加锁方在加锁后未能及时解锁(也未设置超时时间),且该客户端崩溃,可能导致死锁。

(3)存在竞争:由于SETNX命令是对Key的操作,所以在高并发情况下,多个客户端之间仍可能存在竞争,从而影响性能。

单线程执行的,所以性能较高。

缺点

(1)锁无法续期:如果加锁方在加锁后的执行时间较长,而锁的超时时间设置的较短,可能导致锁被误释放。

(2)无法避免死锁:如果加锁方在加锁后未能及时解锁(也未设置超时时间),且该客户端崩溃,可能导致死锁。

(3)存在竞争:由于SETNX命令是对Key的操作,所以在高并发情况下,多个客户端之间仍可能存在竞争,从而影响性能。

(4)setnx不支持可重入,可以借助redission封装的能力实现可重入锁。

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

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

相关文章

基于SSM的车库智能管理平台设计与实现【附源码】

基于SSM的车库智能管理平台设计与实现&#xff08;源码L文说明文档&#xff09; 目录 4 系统设计 4.1 系统概要设计 4.2 系统功能结构设计 4.3 数据库设计 4.3.1 数据库E-R图设计 4.3.2 数据库表结构设计 5 系统实现 5.1用户信息管理 5.2 车…

腾讯云SDK地址生成器

音视频终端 SDK&#xff08;腾讯云视立方&#xff09;将新版连麦管理方案的多个功能集成至 腾讯云视立方控制台 > 连麦管理&#xff0c;便于用户快捷使用&#xff0c;具体分为快速上手、连麦应用、用量统计和地址生成器四个功能页面。更多连麦功能说明&#xff0c;请参见 新…

YOLO11 实例分割模型做行人分割

实例分割是检测和隔离图像中单个对象的关键技术,YOLO11 是执行这项任务的最佳模型之一。在本文中,你将学习如何使用 YOLO11 分割模型有效地在图像中分割人物。我们将涵盖从设置 Python 环境和安装必要的库,到下载测试图像和可视化分割结果的所有内容。通过本教程的学习,你将…

pandas的用法

1.简介&#xff1a; pandas是一个开源的python数据分析库提供了快速&#xff0c;灵活和表达力强的数据结构&#xff0c;使数据清洗和分析工作变得更加简单易行。pandas的核心数据结构是DataFrame和Series 2.DataFrame的基本操作&#xff1a; DataFrame是pandas库中的一个二维…

WebAPI的初步认识

这里局限于Asp.net core Web API。 简单一句话&#xff0c;就是webApi支持我们专注于数据&#xff0c;返回值可以直接返回对象。且支持RestFul风格的编程。RestFul编程就是 资源&#xff08;Resource&#xff09;&#xff1a;将所有的数据和功能都视为资源&#xff0c;每个资源…

Passolo使用教程

作用&#xff1a;汉化软件 Passolo下载地址&#xff1a;https://www.xitongzhijia.net/soft/236115.html 使用步骤 1&#xff1a;新建 2&#xff1a;添加来源 -> 找到需要汉化的exe文件 3&#xff1a;目标语言 -> 添加语言 -> 简体中文 4&#xff1a;名称 -> 自定…

【数据结构】栈和队列 + 经典算法题

目录 前言 一、栈 二、栈的实现 三、栈的循环遍历演示 四、栈的算法题 // 一、队列 二、队列的实现 三、使用演示 四、队列的算法题 总结 前言 本文完整实现了栈和队列的数据结构&#xff0c;以及栈和队列的一些经典算法题&#xff0c;让我们更加清楚了解这两种数据…

W外链平台有什么优势?

W外链作为一种短网址服务&#xff0c;具备多项功能和技术优势&#xff0c;适用于多种场景&#xff0c;以下是其主要特点和优势&#xff1a; 短域名与高级设置&#xff1a;W外链提供了非常短的域名&#xff0c;这有助于提高用户体验&#xff0c;使其在社交媒体分享时更加便捷。…

GNURadio 平台实现拦阻干扰

信号流图如下&#xff1a; 先用带通滤波器截取一段噪声信号源的频谱&#xff0c;流图中为100khz-500khz&#xff0c;带宽为400kHz&#xff0c;再进行调制搬移到期望的信号频率上&#xff0c;流图中为2MHz上。 仿真结果信号频谱图如下所示&#xff1a; 假设被干扰的目标信号为A…

Java基础入门:从人机交互到Java核心概述

掌握CMD与Java开发环境&#xff1a;从基础到实战的全面指南 在当今数字化时代&#xff0c;计算机操作和编程技能已成为不可或缺的基础能力。无论你是刚刚迈入编程世界的新手&#xff0c;还是希望提升自己技术水平的开发者&#xff0c;了解如何高效使用命令行工具&#xff08;如…

接着上一篇stp 实验继续

理论看上一篇&#xff0c;我们直接实验 首先找出&#xff52;&#xff4f;&#xff4f;&#xff54; 桥 很明显 &#xff53;&#xff57;&#xff11; 为&#xff52;&#xff4f;&#xff4f;&#xff54; 桥&#xff0c;所谓&#xff53;&#xff57;&#xff11;  &a…

IDEA上Mybatis介绍和使用

MyBatis是一款优秀的持久层框架&#xff0c;用于简化JDBC的开发。 创建项目 在springboot项目中添加Mybatis和MySQL依赖项。 找到数据库选项&#xff0c;点击新建 -> 数据库源&#xff0c;选择MySQL。 输入完成信息后&#xff0c;可以先进行测试&#xff0c;可以成功连接再…

影刀RPA在智能客服上的运用

随着人工智能技术的不断发展&#xff0c;智能客服系统逐渐成为企业提升服务效率和质量的重要工具。影刀RPA&#xff08;Robotic Process Automation&#xff0c;机器人流程自动化&#xff09;作为一种模拟人类用户行为的技术&#xff0c;通过自动化执行重复性高、规则明确的任务…

2024年MathorCup国家级大数据竞赛,必须要知道的竞赛重点!

2024年MathorCup大数据竞赛正在报名中&#xff0c;在越来越多同学报名参赛的同时&#xff0c;也有非常多的同学来咨询比赛相关问题。 01 比赛的级别是什么&#xff1f; 比赛主办方是中国优选法统筹法与经济数学研究会&#xff08;国家一级学会&#xff09;&#xff0c;因此一…

架构设计笔记-7-系统架构设计基础知识

目录 知识要点 单选 案例分析 1.质量属性 / 管道过滤器 / 数据仓库风格 2.面向对象风格 / 控制环路风格 3.软件架构风格 / 架构风格选择 4.体系结构方案对比 5.面向对象风格 / 基于规则风格 6.解释器风格 / 管道过滤器风格 7.面向对象风格 / 解释器风格 8.软件架构复…

【HarmonyOS】HMRouter使用详解(一)环境配置

背景 在项目中使用官方推荐的Navigation时&#xff0c;需要在所有的页面上都添加一层NavDestination&#xff0c;在代码阅读上会增加多个层级&#xff0c;而且还要在主页面设置对应名字的跳转等问题&#xff0c;配置起来比较繁琐。看到大佬开发的HMRouter使用起来方便简洁&…

sim-lock需求分析总结

1、sim-lock是什么&#xff1f; sim-lock也叫subsidy lock 补贴锁, carrier lock, lock&#xff0c;是带合约的机器&#xff0c;运营商限制你使用其他运营商的sim卡&#xff0c;对于长期客户&#xff0c;出国时可以给运营商打电话要一个临时的unlock code。对于Android Teleeph…

24.数据结构和算法-哈夫曼树及其应用(最优二叉树)

哈夫曼树的基本概念 哈夫曼树的构造算法 哈夫曼树构造算法的实现 理论分析 具体实现 哈夫曼编码 哈夫曼编码的性质 例题 哈夫曼编码的算法实现 哈夫曼编码的应用 文件的编码和解码

页面引导解决方案分享

前言 本文主要介绍的是我们在项目中有时候会遇到需要一步一步引导用户操作的使用场景&#xff0c;类似于新手教学的操作指引&#xff0c;给出的解决方案是Intro.js 库&#xff0c;通过此库创建引导式用户体验。 介绍 Intro.js 是一个轻量级的 JavaScript 库&#xff0c;用于…

论文精读:基于概率教师学习的跨域自适应目标检测(ICML2022)

原文标题&#xff1a;Learning Domain Adaptive Object Detection with Probabilistic Teacher 中文标题&#xff1a;基于概率教师学习的域自适应目标检测 代码地址&#xff1a; GitHub - hikvision-research/ProbabilisticTeacher: An official implementation of ICML 2022 p…