1

2

Redis分布式锁实现方式确实是经典问题,下面我将系统性地分析这个方案及其演进过程,并给出生产级的解决方案。

一、基础方案及其缺陷

1. 初始实现方式

SETNX lock_key unique_value  # 尝试获取锁
EXPIRE lock_key 30           # 设置过期时间

致命缺陷

  • 非原子性操作:如果在SETNX和EXPIRE之间进程崩溃,将导致锁永远无法释放

  • 如下图所示的崩溃时间点会导致死锁:

二、原子性解决方案

1. 单命令原子操作(Redis 2.6.12+)

SET lock_key unique_value NX EX 30  # 原子性获取锁并设置过期时间

参数说明

  • NX:仅当key不存在时设置

  • EX:设置过期时间(秒)

  • PX:设置过期时间(毫秒)

2. 完整Java实现示例

public class RedisDistributedLock {private final JedisPool jedisPool;private final String lockKey;private final int expireTime;public boolean tryLock(String uniqueId) {try (Jedis jedis = jedisPool.getResource()) {String result = jedis.set(lockKey, uniqueId, SetParams.setParams().nx().ex(expireTime));return "OK".equals(result);}}public boolean unlock(String uniqueId) {String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +"   return redis.call('del', KEYS[1]) " +"else " +"   return 0 " +"end";try (Jedis jedis = jedisPool.getResource()) {Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(uniqueId));return Long.valueOf(1).equals(result);}}
}

三、生产环境关键问题处理

1. 锁续期问题(看门狗机制)

问题场景:业务执行时间超过锁过期时间

Redisson解决方案

// Redisson自动续期实现
RLock lock = redisson.getLock("lock");
try {lock.lock();  // 默认30秒,看门狗每10秒续期// 业务逻辑
} finally {lock.unlock();
}

2. 集群环境下的锁失效

问题场景:主节点崩溃,锁未同步到从节点

RedLock算法(Redis官方推荐):

Config config1 = new Config();
config1.useSingleServer().setAddress("redis://node1:6379");
RedissonClient redisson1 = Redisson.create(config1);// 创建多个RLock实例
RLock lock1 = redisson1.getLock("lock");
// ...其他节点// 联锁
RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);
try {lock.lock();// 业务逻辑
} finally {lock.unlock();
}

四、各方案对比分析

方案优点缺点适用场景
SETNX+EXPIRE简单直接非原子性,存在死锁风险已淘汰,不推荐使用
SET NX EX原子性操作无自动续期机制短期锁定简单场景
Redisson普通锁自动续期,API友好单节点故障可能失效单Redis节点环境
Redisson红锁更高的可用性性能开销大,实现复杂高要求的金融级场景
Lua脚本实现灵活性高需要自行处理所有边界情况需要定制化的特殊场景

五、生产环境最佳实践

  1. 锁命名规范

    // 业务:功能:资源 三级命名
    String lockKey = "order:pay:orderId_123456";

  2. 超时时间设置

    • 设置合理的过期时间(通常500ms-5s)

    • 评估业务最大执行时间,设置超时时间 > 最大执行时间 × 2

  3. 重试策略

    int retryCount = 0;
    while (retryCount++ < 3) {if (tryLock()) {try {// 业务逻辑break;} finally {unlock();}}Thread.sleep(100 * retryCount);
    }

  4. 监控指标

    • 锁等待时间

    • 锁持有时间

    • 锁获取失败率

    • 死锁发生次数

六、常见陷阱与规避方法

  1. 误解锁的持有者

    // 错误示范:任何线程都能解锁
    public void unlock() {jedis.del(lockKey);
    }// 正确做法:验证唯一标识
    public void unlock(String uniqueId) {// 使用前面展示的Lua脚本
    }

  2. 锁过期后处理

    try {if (tryLock()) {// 业务执行中锁过期...// 可能导致多个客户端同时进入}
    } finally {// 可能释放其他客户端的锁unlock(); 
    }

    解决方案

    • 实现锁续期机制

    • 使用Redisson等成熟框架

  3. 锁重入问题

    public void methodA() {lock();methodB();  // 需要重入锁unlock();
    }public void methodB() {lock();    // 同一线程再次获取锁// ...unlock();
    }

    解决方案:使用支持可重入的锁实现

七、性能优化建议

  1. 锁粒度控制

    // 粗粒度锁(不推荐)
    lock("order");// 细粒度锁(推荐)
    lock("order:123456");

  2. 锁分段技术

    // 将库存分成16段
    int segment = orderId.hashCode() & 15;
    lock("inventory:" + segment);

  3. 避免长时间持锁

    • 将业务逻辑分为锁内和锁外部分

    • 锁内只做竞争资源的操作

八、扩展思考

  1. 分布式锁的本质

    • 本质上是借助一个外部共享存储系统实现的互斥机制

    • Redis只是其中一种实现方式(其他如Zookeeper、Etcd等)

  2. CAP理论下的选择

    • Redis锁偏向AP(高可用)

    • Zookeeper锁偏向CP(一致性)

  3. 分布式锁的演进趋势

    • 向着更高性能、更易用的方向发展

    • 与云原生技术深度整合(如基于Kubernetes的实现)

通过以上系统性的分析和实践建议,可以构建出健壮可靠的Redis分布式锁方案。对于大多数Java项目,推荐直接使用Redisson框架,它已经处理了各种边界条件和异常情况。

3

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

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

相关文章

Hive自定义函数案例(UDF、UDAF、UDTF)

目录 前提条件 背景 概念及适用场景 UDF&#xff08;User-Defined Function&#xff09; 概念 适用场景 UDAF&#xff08;User-Defined Aggregate Function&#xff09; 概念 适用场景 UDTF&#xff08;User-Defined Table-Generating Function&#xff09; 概念 适…

Go语言的原子操作

当我们想要对某个变量并发安全的修改&#xff0c;除了使用官方提供的mutex&#xff0c;还可以使用sync/atomic包的原子操作&#xff0c;它能够保证对变量的读取或修改期间不被其他的协程所影响。 Golang提供的原子操作都是非侵入式的&#xff0c;由标准库sync/atmoic包提供&am…

QNAP MEMOS 域名访问 SSL(Lucky)

注意&#xff1a;下述是通过ssh、docker-compose方式安装docker的&#xff0c;不是直接在container station中安装的哈&#xff01;&#xff01;&#xff01; 一、编辑docker-compose.yml文件 用“#”号标识的&#xff0c;在保存文件的时候建议去掉&#xff0c;不然有时候会出…

C#实现远程锁屏

前言 这是一次提前下班没有锁屏进而引发的一次思考后的产物&#xff0c;思考的主要场景是当人离开电脑后&#xff0c;怎么能控制电脑锁屏&#xff0c;避免屏幕上的聊天记录被曝光。 首先想到通过系统的电源计划设置闲置超时时间熄屏&#xff0c;这可能是最接近场景的解决方案&a…

[Protobuf]常见数据类型以及使用注意事项

[Protobuf]常见数据类型以及使用注意事项 水墨不写bug 文章目录 一、基本数据类型1、字段2、字段的修饰规则 二、自定义数据类型1、message类型2、enum类型3、Any类型4、oneof类型5、map类型 三、小工具1.hexdump2.decode 四、注意事项 一、基本数据类型 protobuf 支持多种基础…

JS分支和循环

程序的执行顺序 在程序开发中&#xff0c;程序有三种不同的执行顺序 1.顺序执行 2.分支执行 3.循环执行 程序的代码块 <script>//一个代码块{var num11var num22var num3num1num2}//一个休想var info{name:"chen",age:18} 1.if分支语句&#xff08;单分支语句&…

Android 开发 Kotlin 全局大喇叭与广播机制

在 Android 开发中&#xff0c;广播机制就像一个神通广大的 “消息快递员”&#xff0c;承担着在不同组件间传递信息的重任。Kotlin 语言的简洁优雅更使其在广播机制的应用中大放异彩。今天&#xff0c;就让我们一同深入探索 Android 开发中 Kotlin 全局大喇叭与广播机制的奥秘…

rabbitmq AI复习

RabbitMq rabbitmq &#x1f9d1;‍&#x1f4bb; User 帮我复习rabbitmq相关知识&#xff0c;我是一个经验丰富的程序员 &#x1f916; Assistant 好的&#xff01;很高兴能通过这种方式帮你复习或学习 RabbitMQ 的知识。按照你说的流程&#xff0c;我们从完全零基础开始&…

计算机视觉---YOLOv5

YOLOv5理论讲解 一、YOLOv5 整体架构解析 YOLOv5 延续了 YOLO 系列的 单阶段目标检测框架&#xff0c;包含 主干网络&#xff08;Backbone&#xff09;、颈部网络&#xff08;Neck&#xff09; 和 检测头&#xff08;Head&#xff09;&#xff0c;但在结构设计上更注重 轻量化…

C++多重继承详解与实战解析

#include <iostream> using namespace std; //基类&#xff0c;父类 class ClassA { public:void displayA() {std::cout << "Displaying ClassA" << std::endl;}void testFunc(){std::cout << "testFunc ClassA" << std::e…

单细胞注释前沿:CASSIA——无参考、可解释、自动化细胞注释的大语言模型

细胞类型注释是单细胞RNA-seq分析的重要步骤&#xff0c;目前有许多注释方法。大多数注释方法都需要计算和特定领域专业知识的结合&#xff0c;而且经常产生不一致的结果&#xff0c;难以解释。大语言模型有可能在减少人工输入和提高准确性的同时扩大可访问性&#xff0c;但现有…

STM32Cubemx-H7-17-麦克纳姆轮驱动

前言 --末尾右总体的.c和.h 本篇文章把麦克纳姆轮的代码封装到.c和.h&#xff0c;使用者只需要根据轮子正转的方向&#xff0c;在.h处修改定义方向引脚&#xff0c;把轮子都统一正向后&#xff0c;后面的轮子驱动就可以正常了&#xff0c;然后直接调用函数驱动即可。 设置满…

文档核心结构优化(程序C++...)

文档核心结构优化 一、文档核心结构优化二、C关键特性详解框架2.1 从C到C的范式迁移 三、深度代码解析模板3.1 现代C特性分层解析 四、C vs C 关键差异矩阵五、交互式文档设计策略5.1 三维学习路径5.2 代码缺陷互动区 六、现代C特性演进图七、性能优化可视化呈现&#xff08;深…

PyTorch ——torchvision数据集使用

如果下载的很慢&#xff0c;可以试试下面这个

纯前端实现图片伪3D视差效果

作者&#xff1a;vivo 互联网前端团队- Su Ning 本文通过depth-anything获取图片的深度图&#xff0c;同时基于pixi.js&#xff0c;通过着色器编程&#xff0c;实现了通过深度图驱动的伪3D效果。该方案支持鼠标/手势与手机陀螺仪双模式交互&#xff0c;在保证性能的同时&#x…

英语写作中“专注于”focus on、concentrate的用法

Focus on在论文写作中常用&#xff0c;指出研究点&#xff0c;例如&#xff1a; There are three approaches to achieving ID authentication. Our study will focus on ……&#xff08;有三种途径实现身份认证&#xff0c;我们的研究专注于……&#xff09; concentrate &…

go环境配置

下载对应版本的 go 版本 https://go.dev/dl/ 配置 vim ~/.zshrc export GOROOT/usr/local/go export PATH$PATH:$GOROOT/binsource ~/.zshrc >>>>>> go versiongoland 配置&#xff1a; &#x1f50d; 一、什么是GOPATH&#xff1f; GOPATH 是旧的项目结…

AI Agent智能体:底层逻辑、原理与大模型关系深度解析·优雅草卓伊凡

AI Agent智能体&#xff1a;底层逻辑、原理与大模型关系深度解析优雅草卓伊凡 一、AI Agent的底层架构与核心原理 1.1 AI Agent的基本构成要素 AI Agent&#xff08;人工智能代理&#xff09;是一种能够感知环境、自主决策并执行行动的智能系统。其核心架构包含以下关键组件…

【手搓一个原生全局loading组件解决页面闪烁问题】

页面闪烁效果1 页面闪烁效果2 封装一个全局loading组件 class GlobalLoading extends HTMLElement {constructor() {super();this.attachShadow({ mode: open });}connectedCallback() {this.render();this.init();}render() {this.shadowRoot.innerHTML <style>.load…

unix/linux source 命令,其高级使用

就像在物理学中,掌握了基本定律后,我们可以开始研究更复杂的系统和现象,source 的高级用法也是建立在对其基本行为深刻理解之上的。 让我们一起探索 source 的高级应用领域: 1. 条件化加载 (Conditional Sourcing) 根据某些条件来决定是否 source 一个文件,或者 source…