首页 > 数据库 >实现一个简单的redis分布式锁

实现一个简单的redis分布式锁

时间:2024-07-31 14:29:10浏览次数:13  
标签:加锁 String redis jedis requestId 简单 分布式 lockKey 客户端

分布式锁一般有三种实现方式:1. 数据库乐观锁;2. 基于Redis的分布式锁;3. 基于ZooKeeper的分布式锁。

 

为了确保分布式锁可用,至少要确保锁的实现同时满足以下四个条件:

  1. 互斥性。在任意时刻,只有一个客户端能持有锁。
  2. 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。
  3. 具有容错性。只要大部分的Redis节点正常运行,客户端就可以加锁和解锁。
  4. 解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。

 

 


实现方式,

正确的加锁代码

 

public class RedisTool {
 
    private static final String LOCK_SUCCESS = "OK";
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";
 
    /**
     * 尝试获取分布式锁
     * @param jedis Redis客户端
     * @param lockKey 锁
     * @param requestId 请求标识
     * @param expireTime 超期时间
     * @return 是否获取成功
     */
    public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
 
        String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
 
        if (LOCK_SUCCESS.equals(result)) {
            return true;
        }
        return false;
 
    }
 
}

 

错误的加锁代码

public static void wrongGetLock1(Jedis jedis, String lockKey, String requestId, int expireTime) {
 
    Long result = jedis.setnx(lockKey, requestId);
    if (result == 1) {
        // 若在这里程序突然崩溃,则无法设置过期时间,将发生死锁
        jedis.expire(lockKey, expireTime);
    }
 
}

 

 


正确的解锁代码

public class RedisTool {
 
    private static final Long RELEASE_SUCCESS = 1L;
 
    /**
     * 释放分布式锁
     * @param jedis Redis客户端
     * @param lockKey 锁
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
 
        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));
 
        if (RELEASE_SUCCESS.equals(result)) {
            return true;
        }
        return false;
 
    }
 
}
这段Lua代码首先获取锁对应的value值,检查是否与requestId相等,如果相等则删除锁(解锁)。
使用eval()来执行这段Lua脚本可以保证原子性;
源于Redis特性,eval命令执行Lua代码的时候,Lua代码将被当成一个命令去执行,并且直到eval命令执行完成,Redis才会执行其他命令。

 

错误的解锁代码-不判断是否为同一客户端的

public static void wrongReleaseLock1(Jedis jedis, String lockKey) {
    jedis.del(lockKey);
}

 

错误的解锁代码-非原子操作

public static void wrongReleaseLock2(Jedis jedis, String lockKey, String requestId) {
 
    // 判断加锁与解锁是不是同一个客户端
    if (requestId.equals(jedis.get(lockKey))) {
        // 若在此时,这把锁突然不是这个客户端的,则会误解锁
        jedis.del(lockKey);
    }
 
}

场景:客户端A加锁,一段时间之后客户端A解锁,在执行jedis.del()之前,锁突然过期了,此时客户端B尝试加锁成功,然后客户端A再执行del()方法,则将客户端B的锁给解除了。

标签:加锁,String,redis,jedis,requestId,简单,分布式,lockKey,客户端
From: https://www.cnblogs.com/aries-laosi/p/18334540

相关文章

  • 服务注册中心+配置中心-Nacos-微服务核心组件【分布式微服务笔记07】
    服务注册中心+配置中心-Nacos-微服务核心组件【分布式微服务笔记07】服务注册中心+配置中心-NacosNacos有两大功能:注册中心[替代Eureka]+配置中心[替代Config]架构理论基础:CAP理论(支持AP【高可用、分区容错性】和CP【分区容错性和数据一致性】,可以切换)Nacos结构......
  • 使用 LCEL 构建简单的LLM应用程序
    摘要:本文是对自己学习基于LangChain学习LLM开发的记录,根据LangChain官网使用LCEL构建了一个简单的LLM应用程序。1.获取各个LLM模型的api。比如OPENAI、讯飞星火大模型等。本文使用讯飞星火大模型进行开发。使用OPENAI的api显示超额,但是明明有18美元的余额,如果读者知道的话请解......
  • 五分钟,用flask做一个简单的交互页面
    Python作为一个万能且简单的编程语言,其在各个领域都有着很好的表现。其中在Web领域,也有大名鼎鼎的Django和Flask,今天我们就通过Flask,用五分钟写一个简单的交互页面!基本功能1、安装Flask在命令行中输入pipinstallflask即可安装Flask。2、创建Flask应用在P......
  • 【ROS 最简单教程 002/300】ROS 集成开发环境安装 (虚拟机版): Noetic
    ......
  • 简单零配置的本地 HTTPS 签名证书生成工具
    大家好,又见面了,我是GitHub精选君! 背景介绍开发人员在本地测试网站时,常常需要确保与线上环境一致的安全连接(HTTPS)。然而,使用真正的证书颁发机构(CA)颁发的证书来进行本地开发不仅存在安全隐患,而且对于一些特殊域名(比如example.test、localhost或127.0.0.1)来说,甚至是不可能的......
  • redis基础
    下面资料来自黑马程序员-整理课程内容Redis入门Redis数据类型Redis常用命令在Java中操作Redis1.Redis入门1.1Redis简介Redis是一个基于内存的key-value结构数据库。Redis是互联网技术领域使用最为广泛的存储中间件。官网:https://redis.io中文网:https://www.redis.n......
  • [Redis]哨兵
    这次聊聊,Redis的哨兵机制。为什么要有哨兵机制?在Redis的主从架构中,由于主从模式是读写分离的,如果主节点(master)挂了,那么将没有主节点来服务客户端的写操作请求,也没有主节点给从节点(slave)进行数据同步了。这时如果要恢复服务的话,需要人工介入,选择一个「从节点」切换为「主节......
  • java @Cacheable生成的redisKey,出现两个连续的冒号::
    1、参考基于redis2.1.6实现springcache生成的key多出一个冒号2、解决需要对key进行处理,【重点】是computePrefixWith方法config=config.computePrefixWith(cacheName->{returncacheName+StrUtil.COLON;});以下是完整代码实现CacheK......
  • OpenCV实现图搜图简单案例
    一、概述使用OpenCV实现一个简单的图搜索的小功能特点:暴力匹配实现原理:1.将图片集合生成特征描述,并存入文件2.加载目标图像,并生成图像特征描述3.加载图像特征描述文件列表4.图像特征描述和集合中的特征描述列表进行匹配......
  • git简单使用总结
    概述Git是一种分布式版本控制系统。要想深刻理解Git的工作原理,需要理解理解Git的三个存放区域:本地工作目录、暂存区和仓库,也可以称为三棵树,不过在仓库这个地方又可以分为本地仓库和远程仓库。WorkingDirectory:本地工作目录(工作区)Stage(Index):暂存区Reposi......