• 设为首页
  • 收藏本站
  • 积分充值
  • VIP赞助
  • 手机版
  • 微博
  • 微信
    微信公众号 添加方式:
    1:搜索微信号(888888
    2:扫描左侧二维码
  • 快捷导航
    福建二哥 门户 查看主题

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

    发布者: 福建二哥 | 发布时间: 2025-6-19 12:47| 查看数: 146| 评论数: 0|帖子模式

    什么是分布式锁

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

    为什么 Redis 的 SETNX 可以实现分布式锁

    Redis 的
    1. SETNX
    复制代码
    命令(即
    1. SET if Not eXists
    复制代码
    )可以用来实现分布式锁,原因如下:

    • 原子性
      1. SETNX
      复制代码
      是一个原子操作,这意味着在同一时间只有一个客户端能够成功设置键值对。如果键已经存在,
      1. SETNX
      复制代码
      将不会执行任何操作。这种原子性确保了锁的获取和释放是线程安全的。
    • 唯一性
      1. SETNX
      复制代码
      确保了锁的唯一性。只有第一个尝试设置键的客户端能够成功,其他客户端在尝试设置相同的键时会失败。这模拟了锁的“获取”和“释放”行为。
    • 过期时间:通过结合
      1. EXPIRE
      复制代码
      命令或使用
      1. SET
      复制代码
      命令的
      1. EX
      复制代码
      选项,可以为锁设置一个过期时间。这防止了锁被永久占用,即使客户端在持有锁期间崩溃或未能正确释放锁。
    • 分布式环境:Redis 是一个分布式内存数据库,可以在多个节点之间共享数据。因此,使用 Redis 实现的锁可以在分布式系统中的多个节点之间共享,从而实现分布式锁。
    • 高性能:Redis 是一个高性能的数据库,能够处理大量的并发请求。这使得 Redis 非常适合作为分布式锁的实现基础。

    准备工作

    创建一个Spring Boot项目,并引入相关依赖
    1. <dependency>
    2.     <groupId>org.springframework.boot</groupId>
    3.     <artifactId>spring-boot-starter-data-redis</artifactId>
    4. </dependency>
    复制代码
    1. application.yml
    复制代码
    文件中配置 Redis 连接信息:
    1. server:
    2.   port: 8080
    3. spring:
    4.   redis:
    5.     host: xxx.xxx.xxx.xxx
    6.     port: 6379
    7.     password: xxxxxx
    复制代码
    具体实现

    创建分布式锁工具类
    1. import org.springframework.data.redis.core.StringRedisTemplate;
    2. import org.springframework.stereotype.Component;

    3. import java.util.concurrent.TimeUnit;

    4. @Component
    5. public class DistributedLock {

    6.     private final StringRedisTemplate redisTemplate;

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

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

    24.     /**
    25.      * 释放分布式锁
    26.      *
    27.      * @param lockKey   锁的键
    28.      * @param requestId 请求标识,用于确保只有锁的持有者才能释放锁
    29.      * @return 如果成功释放锁,返回 true;否则返回 false
    30.      */
    31.     public boolean releaseLock(String lockKey, String requestId) {
    32.         // 获取当前锁的值
    33.         String currentValue = redisTemplate.opsForValue().get(lockKey);
    34.         // 检查当前锁的值是否等于请求标识,确保只有锁的持有者才能释放锁
    35.         if (currentValue != null && currentValue.equals(requestId)) {
    36.             // 删除锁键
    37.             return redisTemplate.delete(lockKey);
    38.         }
    39.         return false;
    40.     }
    41. }
    复制代码
    创建业务类用来测试
    1. import com.wh.demo01.demos.web.utils.DistributedLock;
    2. import org.springframework.beans.factory.annotation.Autowired;
    3. import org.springframework.stereotype.Service;

    4. import java.util.Date;

    5. @Service
    6. public class MyRedisService {

    7.     private final DistributedLock distributedLock;

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

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

    20.         try {
    21.             // 尝试获取锁
    22.             if (distributedLock.acquireLock(lockKey, requestId, expireTime)) {
    23.                 // 获取到锁,执行需要同步的操作
    24.                 System.out.println(new Date() + "获取锁成功");
    25.                 // 模拟业务操作
    26.                 Thread.sleep(5000);
    27.             } else {
    28.                 System.out.println(new Date() + "获取锁失败");
    29.             }
    30.         } catch (InterruptedException e) {
    31.             Thread.currentThread().interrupt();
    32.         } finally {
    33.             // 确保锁在操作完成后被释放
    34.             distributedLock.releaseLock(lockKey, requestId);
    35.         }
    36.     }
    37. }
    复制代码
    创建Controller
    1. import com.wh.demo01.demos.web.service.MyRedisService;
    2. import org.springframework.beans.factory.annotation.Autowired;
    3. import org.springframework.web.bind.annotation.GetMapping;
    4. import org.springframework.web.bind.annotation.RequestMapping;
    5. import org.springframework.web.bind.annotation.RestController;

    6. @RestController
    7. @RequestMapping("/redis")
    8. public class RedisController {

    9.     @Autowired
    10.     private MyRedisService service;

    11.     @GetMapping("/test")
    12.     public void TestRedis(){
    13.         service.someMethod();
    14.     }
    15. }
    复制代码
    整个项目结构如下:

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

    使用接口调试工具分别向
    1. 8080
    复制代码
    1. 8081
    复制代码
    端口发送请求:


    结果如下:


    可见在分布式锁的影响下,
    1. someMethod
    复制代码
    方法在10秒内只能被调用一次。
    到此这篇关于Redis使用SETNX命令实现分布式锁的文章就介绍到这了,更多相关Redis SETNX实现分布式锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

    来源:https://www.jb51.net/database/334285d5q.htm
    免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有账号?立即注册

    ×

    最新评论

    浏览过的版块

    QQ Archiver 手机版 小黑屋 福建二哥 ( 闽ICP备2022004717号|闽公网安备35052402000345号 )

    Powered by Discuz! X3.5 © 2001-2023

    快速回复 返回顶部 返回列表