Redis实现分布式锁的原理

一、为什么需要分布式锁

在分布式系统 中,多个节点或服务实例可能同时修改同一份共享资源(例如数据库、库存、订单),

为防止并发修改、超卖、数据不一致 ,我们需要一种 跨节点的"互斥"机制。

在单机中可用 synchronized 或 ReentrantLock,

但在多机(分布式)环境下,它们无法保证互斥性。

这时就需要分布式锁。


⚙️ 二、Redis 分布式锁的基本原理

Redis 本身是一个单线程 、支持原子操作的 KV 数据库,非常适合实现分布式锁。

核心思想:

使用 Redis 的原子命令 SETNX(Set if Not Exists)

  • 过期时间(防止死锁)
    来确保在分布式环境中,同一时刻只有一个客户端能获得锁。

🔑 三、最常见的实现方式(单节点版本)

🧠 思路:

  1. 加锁 :使用 SET key value NX PX expireTime

    • NX:只有 key 不存在时才能设置成功(防止被覆盖)
    • PX:设置过期时间(毫秒)
  2. 释放锁:只有锁的持有者才能删除锁(防止误删)

🔑 四、 Redisson 高级实现(推荐)

Redisson 是官方推荐的 Java Redis 客户端之一,封装了稳定的分布式锁逻辑

Redisson 实现了:

  • 自动续期机制(Watchdog)
  • 支持 RedLock 多节点锁算法
  • 防止主从切换导致锁丢失

RedLock 的基本思想

假设有 5 个独立的 Redis 节点(N = 5),RedLock 的核心流程是:

  1. 客户端向所有 Redis 节点尝试获取锁**,每个节点的锁具有 唯一标识(UUID) 和 过期时间(TTL)。

  2. 获取锁成功的条件:

    • 客户端在多数节点(> N/2,比如至少3个节点) 成功获取到锁。
    • 并且总耗时 < TTL。
  3. 业务执行完毕释放锁:

    • 客户端向获取成功的 Redis 节点发送解锁请求。
    • 每个节点只删除与自己 UUID 匹配的锁,保证安全释放。
  4. 如果获取锁失败(未超过多数节点或超时),回滚已获取的锁。

js 复制代码
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

public class RedissonManager {

    private static RedissonClient redissonClient;

    static {
        Config config = new Config();
        // Redis 集群模式配置
        config.useClusterServers()
              .addNodeAddress("redis://127.0.0.1:7000", "redis://127.0.0.1:7001", "redis://127.0.0.1:7002");
        redissonClient = Redisson.create(config);
    }

    public static RedissonClient getClient() {
        return redissonClient;
    }
}
js 复制代码
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;

import java.util.concurrent.TimeUnit;

public class DistributedLockExample {

    public static void main(String[] args) {
        RedissonClient redisson = RedissonManager.getClient();

        // 获取锁对象
        RLock lock = redisson.getLock("myLock");

        try {
            // 尝试加锁,最多等待3秒,锁超时10秒自动释放
            boolean isLocked = lock.tryLock(3, 10, TimeUnit.SECONDS);
            if (isLocked) {
                try {
                    // 加锁成功,执行业务逻辑
                    System.out.println("获得锁,执行业务逻辑");
                    Thread.sleep(5000); // 模拟业务操作
                } finally {
                    lock.unlock(); // 释放锁
                    System.out.println("释放锁");
                }
            } else {
                System.out.println("获取锁失败,其他线程在执行");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
相关推荐
实名上网宋凯宣7 小时前
Django
后端·python·django
利威尔xu9 小时前
Go IM 源码实战(14):本地缓存快 3000 倍,失效却难 10 倍
后端·缓存·微服务·面试·golang
逃逸线LOF10 小时前
Ribbon负载均衡原理(Irule组件、源码分析)
后端·spring cloud·ribbon
慢云智慧空间10 小时前
从设备智能到空间理解,空间智能终端解决方案商如何重新定义行业标准
后端
利威尔xu10 小时前
Go IM 源码实战(13):30 个会话只有 2 个红点,未读数错在三条路径
redis·后端·微服务·面试·golang
薛定谔的猫-菜鸟程序员11 小时前
软件架构设计师开发资料大全(从入门到精通)
后端·python·系统架构·flask·架构设计师
IT_陈寒12 小时前
SpringBoot多环境配置把我坑惨了,这个踩坑经验你最好看看
前端·人工智能·后端
码事漫谈13 小时前
DeepSeek 搞了个 "Super-agent",但不是超级智能
后端
吴凡wudianmei13 小时前
从 0 搭一个 AI Agent(四):SSE 跑通之后,三处“契约错配”与它们的修复边界
前端·后端·aigc
小兔子13 小时前
defer 的参数在写下那一刻就求值了,循环里这样写会踩坑
后端