Redis实现分布式锁的原理

Redis分布式锁基于其单线程执行命令的特性,通过原子操作实现多节点间的互斥访问。下面从原理、实现、问题及优化四个方面详细解析:

1.原子性与互斥性

Redis分布式锁的核心是原子性操作

  1. 获取锁:使用SET key value NX EX timeout命令

    • NX(Not eXists):仅当key不存在时设置成功
    • EX timeout:设置过期时间,防止死锁
    • 原子性:Redis单线程执行命令,确保多客户端并发请求时只有一个能成功
  2. 释放锁:先验证锁持有者再删除

    • 必须使用Lua脚本保证原子性,避免误删其他线程的锁
-- 释放锁的Lua脚本
if redis.call('get', KEYS[1]) == ARGV[1] thenreturn redis.call('del', KEYS[1])
elsereturn 0
end
2.分布式锁的实现步骤

1. 获取锁流程

  • 客户端生成唯一标识(如UUID)作为锁的值
  • 执行SET lock_key unique_id NX EX 10(10秒过期)
  • 返回OK表示获取锁成功,否则失败

2. 释放锁流程

  • 客户端携带锁的唯一标识调用Lua脚本
  • 脚本先检查锁的值是否与传入标识一致
  • 一致则删除锁,返回1;不一致返回0

示例

1. 添加依赖

pom.xml中添加Spring Data Redis依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2. 配置Redis连接

application.yml中配置Redis服务器信息:

spring:redis:host: localhostport: 6379password: yourpassword  # 如果有密码timeout: 5000mslettuce:pool:max-active: 8max-wait: -1msmax-idle: 8min-idle: 0
3. 创建分布式锁接口

定义锁的基本操作:

public interface RedisLock {/*** 尝试获取锁* @param lockKey 锁的键* @param requestId 请求标识(用于释放锁时校验)* @param expireTime 锁的过期时间* @param timeUnit 时间单位* @return 是否成功获取锁*/boolean tryLock(String lockKey, String requestId, long expireTime, TimeUnit timeUnit);/*** 释放锁* @param lockKey 锁的键* @param requestId 请求标识* @return 是否成功释放锁*/boolean releaseLock(String lockKey, String requestId);
}
4. 实现分布式锁(重点)

使用RedisTemplate实现锁操作,关键在于:

  • 获取锁:使用setIfAbsent原子操作
  • 释放锁:使用Lua脚本保证原子性
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;import java.util.Collections;
import java.util.concurrent.TimeUnit;@Component
public class RedisLockImpl implements RedisLock {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;// 释放锁的Lua脚本:先验证锁的持有者,再删除锁private static final DefaultRedisScript<Long> RELEASE_LOCK_SCRIPT;static {RELEASE_LOCK_SCRIPT = new DefaultRedisScript<>();RELEASE_LOCK_SCRIPT.setScriptText("if redis.call('get', KEYS[1]) == ARGV[1] then " +"    return redis.call('del', KEYS[1]) " +"else " +"    return 0 " +"end");RELEASE_LOCK_SCRIPT.setResultType(Long.class);}@Overridepublic boolean tryLock(String lockKey, String requestId, long expireTime, TimeUnit timeUnit) {// 核心方法:原子性地设置锁和过期时间Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, expireTime, timeUnit);return result != null && result;}@Overridepublic boolean releaseLock(String lockKey, String requestId) {// 使用Lua脚本保证原子性Long result = redisTemplate.execute(RELEASE_LOCK_SCRIPT,Collections.singletonList(lockKey),requestId);return result != null && result == 1L;}
}
5. 使用分布式锁
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.UUID;
import java.util.concurrent.TimeUnit;@Service
public class OrderService {@Autowiredprivate RedisLock redisLock;public void createOrder(String orderId) {String lockKey = "order-lock:" + orderId;String requestId = UUID.randomUUID().toString();boolean locked = false;try {// 尝试获取锁,设置过期时间为10秒locked = redisLock.tryLock(lockKey, requestId, 10, TimeUnit.SECONDS);if (locked) {// 获得锁成功,执行关键业务逻辑System.out.println("获取锁成功,开始处理订单: " + orderId);// 模拟业务处理Thread.sleep(2000);} else {// 获得锁失败,处理失败逻辑System.out.println("获取锁失败,稍后重试或执行其他策略");}} catch (Exception e) {e.printStackTrace();} finally {// 无论如何都尝试释放锁,确保不会死锁if (locked) {redisLock.releaseLock(lockKey, requestId);}}}
}

setIfAbsent方法

RedisTemplate.opsForValue().setIfAbsent(key, value, timeout, unit) 是实现分布式锁的核心方法,它对应Redis的命令:

SET key value NX EX timeout

关键点

  1. 原子性:该方法会原子性地完成三个操作:

    • 检查key是否存在
    • 如果不存在,则设置key的值
    • 同时设置key的过期时间
  2. 防止死锁

    • 必须设置过期时间,确保即使持有锁的进程崩溃,锁也会自动释放
    • 过期时间不宜过短(避免业务未完成锁就过期)或过长(影响性能)
  3. 唯一标识

    • value使用唯一的requestId(如UUID),用于标识锁的持有者
    • 释放锁时必须验证requestId,防止误删其他线程的锁

释放锁的原子性问题

释放锁时不能简单地直接删除key,必须先验证锁的持有者:

// 错误示例(非原子操作,有竞态条件)
if (redis.get(lockKey).equals(requestId)) {redis.delete(lockKey);
}// 正确方式:使用Lua脚本保证原子性
Long result = redisTemplate.execute(RELEASE_LOCK_SCRIPT, Collections.singletonList(lockKey), requestId);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/87285.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/87285.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/87285.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux升级降级内核实验

✅实验环境 vmware workstation 17 centos7.9 下载链接&#xff1a; https://vault.centos.org/7.9.2009/isos/x86_64/ ubuntu24.04 下载链接&#xff1a; https://old-releases.ubuntu.com/releases/24.04/ ✅实验目的 为了解决日常环境部署中某些驱动软件依赖特定内…

华为云开始了“开发者空间 AI Agent 开发”活动

引言 今天在华为云App上偶然看到一个新活动&#xff1a;Developer Events_Developer Alliance-Huawei Cloud。这个活动要求开发者可结合自己的工作实践&#xff0c;须在华为开发者空间内完成应用构建&#xff0c;应用构建类型和主题为AI Agent应用开发。 AI Agent平台 华为开…

2025.6.26总结

今天和我做同一业务得同事进行了工作交接&#xff0c;主要给我讲了怎么去执行自动化。包括性能自动化&#xff0c;API自动化&#xff0c;UI自动化&#xff0c;除了UI自动化要写些代码&#xff0c;其他跑得话也就在工具上配个参数&#xff0c;就是个搬砖得活&#xff0c;没太大技…

ip网络基础

交换机工作原理&#xff1a; 自主学习mac地址并成mac地址表 根据mac地址表再进行单播、广播转发 主机通信原理&#xff08;局域网&#xff09;&#xff1a; 需要了解arp协议 拓扑图&#xff1a; 首先&#xff0c;我们观察icmp数据包&#xff0c;发现缺少目标mac地址&#…

AI大模型如何重塑软件开发流程?

文章目录 每日一句正能量前言一、AI大模型的定义与特点&#xff08;一&#xff09;定义&#xff08;二&#xff09;特点 二、AI大模型在软件开发中的应用场景&#xff08;一&#xff09;代码自动生成&#xff08;二&#xff09;智能测试&#xff08;三&#xff09;需求分析与设…

Kafka与RabbitMQ相比有什么优势?

大家好&#xff0c;我是锋哥。今天分享关于【Kafka与RabbitMQ相比有什么优势&#xff1f;】面试题。希望对大家有帮助&#xff1b; Kafka与RabbitMQ相比有什么优势&#xff1f; 超硬核AI学习资料&#xff0c;现在永久免费了&#xff01; Kafka与RabbitMQ在消息队列的设计和应…

LeetCode 2090. 半径为 k 的子数组平均值

题目链接 2090. 半径为 k 的子数组平均值 题目描述 给定一个下标从 0 开始的整数数组 nums 和整数 k&#xff0c;构建并返回一个长度为 n 的数组 avgs&#xff0c;其中 avgs[i] 表示以下标 i 为中心、半径为 k 的子数组的平均值。具体规则如下&#xff1a; 无效位置&#x…

深入理解C++11原子操作:从内存模型到无锁编程

文章目录 C并发编程的新纪元内存模型基础&#xff1a;可见性与有序性数据竞争的根源happens-before关系memory_order枚举详解1. memory_order_relaxed2. memory_order_acquire/memory_order_release3. memory_order_seq_cst 原子操作详解std::atomic模板核心原子操作1. 读取与存…

DQL-1-基础查询

基础查询 DQL-1-基础查询 基础查询DQL - 介绍DQL - 语法DQL - 基本查询案例 DQL - 介绍 SQL 英文全称是 Data Query Language, 数据查询语言, 用来查询数据库中表的记录 查询关键字: SELECT DQL - 语法 SELECT 字段列表FROM 表名列表WHERE条件列表GROUP BY分组字段列表HAVI…

Prompt 精通之路(七)- 你的终极 AI 宝典:Prompt 精通之路系列汇总

你的终极 AI 宝典&#xff1a;Prompt 精通之路系列汇总 标签&#xff1a; #Prompt指南 #AI学习资源 #速查手册 #ChatGPT #系列总结 &#x1f680; Prompt 精通之路&#xff1a;系列文章导航 第一篇&#xff1a;AI 时代的新语言&#xff1a;到底什么是 Prompt&#xff1f;为什么…

P27:RNN实现阿尔茨海默病诊断

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 一、过程解读 PyTorch 实战&#xff1a;阿尔茨海默病数据预测模型 今天&#xff0c;我将带大家一起探索一个基于 PyTorch 的深度学习小项目——利用 RNN 模…

HakcMyVM-Arroutada

信息搜集 主机发现 ┌──(kali㉿kali)-[~] └─$ nmap -sn 192.168.21.0/24 Starting Nmap 7.95 ( https://nmap.org ) at 2025-07-01 07:13 EDT Nmap scan report for 192.168.21.11 Host is up (0.00062s latency). MAC Address: 08:00:27:4E:CC:FB (PCS Systemtechnik/Or…

TEXT Submitting Solutions

前言 USACO 训练项目配备了一个自动评分系统&#xff0c;用于批改你的作业题目。你可以直接在题目页面提交你的程序&#xff1b;系统会对程序进行编译和评分&#xff0c;几秒钟内就能将结果反馈给你。 支持的语言有 C、C&#xff08;含 C11 和 C14&#xff09;、PASCAL、Pyth…

Reactor 瞬态错误

在响应式编程中&#xff0c;retryWhen 操作符通过 RetrySignal 接口提供了对重试行为的精细控制&#xff0c;特别是在处理 瞬态错误&#xff08;transient errors&#xff09; 时。瞬态错误是指那些在一段时间内发生&#xff0c;但随后会自行恢复的错误&#xff0c;例如网络请求…

基于 SpringBoot+Vue.js+ElementUI 的小型超市商品管理系统设计与实现7000字论文设计

摘要 本论文设计并实现了一个基于 SpringBoot、Vue.js 和 ElementUI 的小型超市商品管理系统。该系统旨在为小型超市提供一个高效、便捷的商品管理解决方案&#xff0c;实现商品信息的录入、查询、修改、删除等功能&#xff0c;同时支持库存管理、销售统计等业务需求。论文首先…

Kerberos 认证协议解析

文章目录 概述核心概念认证流程阶段一&#xff1a;Client -> AS&#xff0c;获取 TGT阶段二&#xff1a;Client -> TGS&#xff0c;获取服务票据阶段三&#xff1a;Client -> Server&#xff0c;请求服务 核心安全机制优缺点分析优势局限性 实践与排错关键配置 (krb5.…

【设计模式07】适配器

前言 实现目标&#xff0c;组合源&#xff0c;写个适配方法&#xff0c;适用于没办法改变源&#xff0c;但又想实现目标类。我暂时还没使用到过&#xff0c;但感觉用处还是蛮大的 UML类图 代码示例 package com.sw.learn.pattern.C_structre.a_adapter;public class Main {//…

SPI、I2C和UART三种串行通信协议的--------简单总结

目录 一、3种协议的对比二、典型应用场景三、选型建议 以下是SPI、I2C和UART三种串行通信协议的对比分析及适用场景总结&#xff1a; 一、3种协议的对比 . 对比其他接口 特性ICSPIUART信号线数量2&#xff08;SCL SDA&#xff09;4&#xff08;SCK MOSI MISO SS/CS&…

VUE admin-element 后台管理系统三级菜单实现缓存

VUE admin-element 后台管理系统三级菜单实现缓存 框架无法直接实现三级菜单页面缓存&#xff0c;原因是由于直接缓存时没有把上级路由文件名称缓存进去&#xff0c;所以在框架基础上参考部分文章进行了一些改造 菜单文件&#xff0c;三级菜单引用文件路径修改&#xff0c;在…

【笔记】Windows 安装 Gemini CLI

2025 年 07 月 02 日 Windows 安装 Gemini CLI google-gemini/gemini-cli&#xff1a;一个开源的 AI 代理&#xff0c;可将 Gemini 的强大功能直接引入您的终端。 一、前置条件 系统要求&#xff1a;Windows 7 及以上版本。 Node.js 环境&#xff1a;Gemini CLI 基于 Node.js …