首页 > 数据库 >使用Redis的SETNX命令实现分布式锁

使用Redis的SETNX命令实现分布式锁

时间:2024-09-23 15:34:55浏览次数:1  
标签:org Redis springframework requestId SETNX import lockKey 分布式

什么是分布式锁
分布式锁是一种用于在分布式系统中控制多个节点对共享资源进行访问的机制。在分布式系统中,由于多个节点可能同时访问和修改同一个资源,因此需要一种方法来确保在任意时刻只有一个节点能够对资源进行操作,以避免数据不一致或冲突。分布式锁就是用来实现这种互斥访问的工具。

为什么 Redis 的 SETNX 可以实现分布式锁
Redis 的 SETNX 命令(即 SET if Not eXists)可以用来实现分布式锁,原因如下:

原子性:SETNX 是一个原子操作,这意味着在同一时间只有一个客户端能够成功设置键值对。如果键已经存在,SETNX 将不会执行任何操作。这种原子性确保了锁的获取和释放是线程安全的。

唯一性:SETNX 确保了锁的唯一性。只有第一个尝试设置键的客户端能够成功,其他客户端在尝试设置相同的键时会失败。这模拟了锁的“获取”和“释放”行为。

过期时间:通过结合 EXPIRE 命令或使用 SET 命令的 EX 选项,可以为锁设置一个过期时间。这防止了锁被永久占用,即使客户端在持有锁期间崩溃或未能正确释放锁。

分布式环境:Redis 是一个分布式内存数据库,可以在多个节点之间共享数据。因此,使用 Redis 实现的锁可以在分布式系统中的多个节点之间共享,从而实现分布式锁。

高性能:Redis 是一个高性能的数据库,能够处理大量的并发请求。这使得 Redis 非常适合作为分布式锁的实现基础。

准备工作
创建一个Spring Boot项目,并引入相关依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
1
2
3
4
在application.yml文件中配置 Redis 连接信息:

server:
port: 8080
spring:
redis:
host: xxx.xxx.xxx.xxx
port: 6379
password: xxxxxx
1
2
3
4
5
6
7
具体实现
创建分布式锁工具类

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Component
public class DistributedLock {

private final StringRedisTemplate redisTemplate;

// 通过构造函数注入 StringRedisTemplate
public DistributedLock(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}

/**
* 尝试获取分布式锁
*
* @param lockKey 锁的键
* @param requestId 请求标识,用于区分不同的锁持有者
* @param expireTime 锁的过期时间,单位为毫秒
* @return 如果成功获取锁,返回 true;否则返回 false
*/
public boolean acquireLock(String lockKey, String requestId, long expireTime) {
// 使用 setIfAbsent 方法尝试设置键值对,如果键不存在则设置成功并返回 true,否则返回 false
Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, expireTime, TimeUnit.MILLISECONDS);
return result != null && result;
}

/**
* 释放分布式锁
*
* @param lockKey 锁的键
* @param requestId 请求标识,用于确保只有锁的持有者才能释放锁
* @return 如果成功释放锁,返回 true;否则返回 false
*/
public boolean releaseLock(String lockKey, String requestId) {
// 获取当前锁的值
String currentValue = redisTemplate.opsForValue().get(lockKey);
// 检查当前锁的值是否等于请求标识,确保只有锁的持有者才能释放锁
if (currentValue != null && currentValue.equals(requestId)) {
// 删除锁键
return redisTemplate.delete(lockKey);
}
return false;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
创建业务类用来测试

import com.wh.demo01.demos.web.utils.DistributedLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;

@Service
public class MyRedisService {

private final DistributedLock distributedLock;

// 通过构造函数注入 DistributedLock
@Autowired
public MyRedisService(DistributedLock distributedLock) {
this.distributedLock = distributedLock;
}

/**
* 模拟需要同步执行的方法
*/
public void someMethod() {
String lockKey = "myLockKey";
String requestId = "uniqueRequestId";
long expireTime = 10000; // 10 seconds

try {
// 尝试获取锁
if (distributedLock.acquireLock(lockKey, requestId, expireTime)) {
// 获取到锁,执行需要同步的操作
System.out.println(new Date() + "获取锁成功");
// 模拟业务操作
Thread.sleep(5000);
} else {
System.out.println(new Date() + "获取锁失败");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
// 确保锁在操作完成后被释放
distributedLock.releaseLock(lockKey, requestId);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
创建Controller

import com.wh.demo01.demos.web.service.MyRedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/redis")
public class RedisController {

@Autowired
private MyRedisService service;

@GetMapping("/test")
public void TestRedis(){
service.someMethod();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
整个项目结构如下:

使用idea的复制配置功能将该服务复制一份,并指定端口为8081,模拟分布式服务:

使用接口调试工具分别向8080和8081端口发送请求:


结果如下:


可见在分布式锁的影响下,someMethod方法在10秒内只能被调用一次。
————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/qq_39354140/article/details/140514514

标签:org,Redis,springframework,requestId,SETNX,import,lockKey,分布式
From: https://www.cnblogs.com/ILoveJackSparrow/p/18427149

相关文章

  • Redis 内存突增时,如何定量分析其内存使用情况am
    合集-Redis(1)1.Redis内存突增时,如何定量分析其内存使用情况09-23收起背景最近碰到一个case,一个Redis实例的内存突增,used_memory最大时达到了78.9G,而该实例的maxmemory配置却只有16G,最终导致实例中的数据被大量驱逐。以下是问题发生时INFOMEMORY的部分输出内容。# M......
  • Redis锁如何续期?Redis锁超时,任务没完怎么办?
    解决方案在使用Redis作为分布式锁的存储时,如果一个任务需要长时间执行,并且在这段时间内锁会过期,那么就需要一种机制来自动延长锁的有效期,即续期。通常情况下,可以通过以下几种方式来实现Redis锁的续期:使用Lua脚本实现续期一种常见的做法是使用Lua脚本来实现锁的续期。Redis支持在服......
  • Redis简单介绍与安装应用
            在当今的互联网时代,数据的快速存取和处理变得至关重要。Redis,作为一种高性能的键值存储系统,已经成为许多开发者和企业的首选。本文将简要介绍Redis的基本概念、工作原理以及其在实际应用中的一些典型用例。一、简介1)概念        Redis(RemoteDictio......
  • 国产化:TongRDS替代Redis
    背景:国产化要求,内存数据缓存中间件要换国产产品,这里简单记录一下替换过程,项目是springboot微服务结构。官方文档比较全,这里只是个人记录的最简化的版本。1安装企业版TongRDS分为2个节点,我拿到的版本就是企业版,所以下面的都默认是企业版。分为中心节点和服务节点。安装......
  • Redis Sentinel:秒杀系统背后的可靠性保障神器!
    哈喽,大家好呀!我是小米,今天我想和大家聊聊如何在个人项目中保证系统的可靠性,尤其是用Redis哨兵模式来保障高可用性。相信很多小伙伴在开发中遇到过Redis挂掉的情况,特别是在高并发场景下,一旦主服务器下线,整个系统可能会因此瘫痪。那我们该如何应对这个问题呢?今天就带大家深入了解......
  • 【解决方案】Java 互联网项目中常见的 Redis 缓存应用场景
    目录前言一、常见key-value二、时效性强三、计数器相关四、高实时性五、排行榜系列六、文章小结前言在笔者3年的Java一线开发经历中,尤其是一些移动端、用户量大的互联网项目,经常会使用到Redis作为缓存中间件的基本工具来解决一些特定的问题。下面是笔者总结梳理的一些常......
  • Redis 内存突增时,如何定量分析其内存使用情况
    背景最近碰到一个case,一个Redis实例的内存突增,used_memory最大时达到了78.9G,而该实例的maxmemory配置却只有16G,最终导致实例中的数据被大量驱逐。以下是问题发生时INFOMEMORY的部分输出内容。# Memoryused_memory:84716542624used_memory_human:78.90Gused_memory_rss:1......
  • Lua 脚本在 Redis 中能够保证操作不会被其他指令插入或打扰 ??
    Lua脚本在Redis中能够保证操作不会被其他指令插入或打扰,主要通过以下机制实现:1.Redis单线程模型2. 脚本执行锁定当执行Lua脚本时,Redis会自动锁定所有在脚本中访问的键。这个过程可以分为几个步骤:获取锁:在脚本执行前,Redis会检查脚本中访问的键。如果有键已经被其他......
  • Redis从基础到实战总结+Redisson分布式锁小结
    一、NoSQL和RDBMS的区别传统的rdbms结构化组织SQL数据和关系都存储在单独的表中操作语言是数据库定义语言严格的一致性基础的事务NoSql不仅仅是数据没有固定的语言键值对存储,列存储、文档存储、图形数据库最终一致性cpa定理和base高性能,高可用,高可扩二、NoSql的四大分类......
  • redis八股
    redis八股文基础内容Redis面试题,56道Redis八股文(1.9万字97张手绘图),面渣逆袭必看......