Redis实现分布式锁的原理

一、为什么需要分布式锁

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

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

在单机中可用 synchronizedReentrantLock

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

这时就需要分布式锁


⚙️ 二、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();
        }
    }
}
相关推荐
企业数字化笔记1 小时前
固定资产历史数据怎么导入系统?Excel模板、字段映射和数据校验
android·java·数据库·后端
西瓜太郎12341 小时前
request_id 如何串起请求、路由、计费和日志
后端·python
by组态1 小时前
Ricon组态系统通信配置指南
前端·后端·物联网
sbjdhjd1 小时前
ThinkPHP 5.0.10 缓存写入型 RCE 复盘:从手动搭建、换行绕过到源码单步验证 | 05
java·后端·安全·spring·网络安全·数据挖掘·php
LEE1 小时前
Agent 的控制权,为什么正在回到模型手里?
前端·后端
沃夫上校1 小时前
跨域这件事,其实浏览器在替你挡刀
前端·后端·nginx
用户298698530141 小时前
Python 将 Word 文档转换为图片的实践指南
后端·python·api
旺仔不是程序员1 小时前
致命索引失效:索引列运算与函数索引不匹配
数据库·后端·面试
老孙讲技术2 小时前
把连锁门店轮询抓拍和遮挡叫醒接进督导台-setDeviceSnapEnhanced与setMessageCallback
后端·物联网·音视频开发
EatFan2 小时前
前后端分离项目中 Token 到底应该怎么设计?Access Token + Refresh Token 实战
java·前端·后端