【必会面试题】Redis如何实现分布式锁?

目录

    • 一、为什么要使用分布式锁?
    • 二、什么是分布式锁?
    • 三、什么是Redis分布式锁?
    • 四、如何实现Redis分布式锁?
      • 1. 基础方法:SETNX + EXPIRE
      • 2. Lua 脚本
      • 3. Redisson 实现
      • 4. Redlock 算法
    • 五、Java+Redis分布式锁
      • 1. 基于Jedis的简单实现
      • 2. 使用Redisson实现

一、为什么要使用分布式锁?

为了解决分布式系统中并发控制和资源同步的问题。

  • 传统的单机系统:使用如Java中的synchronized关键字或ReentrantLock等机制来实现线程间的同步,确保同一时间只有一个线程能访问共享资源。
  • 分布式系统环境:服务被部署在多台机器上,传统的本地锁机制无法跨越不同的JVM或服务器进程,因此需要引入分布式锁来保证在分布式环境下的资源访问控制。

二、什么是分布式锁?

分布式锁是一种在分布式系统中协调多进程或线程对共享资源进行访问控制的机制。它确保同一时刻只有一个进程或线程能够访问特定资源,从而防止并发操作导致的数据不一致问题。在分布式环境中,由于系统分布在不同的网络节点上,传统的单机锁机制不再适用,因此需要引入分布式锁。

三、什么是Redis分布式锁?

Redis分布式锁是利用Redis作为中间件实现的一种分布式锁机制。Redis凭借其高性能、高可用性和丰富的数据结构特性,成为实现分布式锁的理想选择。通过Redis提供的命令,如SETNX(Set if Not eXists)、GETSETEXPIRE等,可以构建出既简单又高效的锁机制。

四、如何实现Redis分布式锁?

当然,让我们深入探讨如何使用 Redis 实现分布式锁,特别是通过几种典型方法,并强调每个方法的关键细节和最佳实践。

1. 基础方法:SETNX + EXPIRE

这是实现分布式锁最基础的方式,利用 Redis 的 SETNX 命令尝试设置一个键值对,如果键不存在则设置成功,表示获取锁成功。同时,为了防止锁持有者崩溃导致的死锁,需要设置一个过期时间(使用 EXPIREPEXPIRE 命令)。

问题:这种方法的问题在于 SETNXEXPIRE 是两个独立的命令(即非原子性的),如果在这两个操作之间程序崩溃,可能会导致锁没有设置过期时间,造成死锁。
解决:从 Redis 2.6.12 版本开始,SET 命令支持在一次操作中完成设置键值、设置过期时间和检查键是否存在,所以生产环境中推荐使用SET命令的NX和PX参数来实现原子性设置值和过期时间。

SET my_lock_key "lock_value" NX PX 5000

2. Lua 脚本

为了解决 SETNXEXPIRE的安全性问题,也可以使用 Lua 脚本将设置锁和设置过期时间的操作封装在一个原子操作中,保证执行 Lua 脚本的原子性。

Lua 脚本示例

-- KEYS[1] 是锁的键名
-- ARGV[1] 是锁的过期时间,单位为毫秒
-- ARGV[2] 是一个随机值,作为锁的标识符,防止误删其他客户端的锁if redis.call("setnx", KEYS[1], ARGV[2]) == 1 thenredis.call("pexpire", KEYS[1], ARGV[1])return 1 -- 返回1表示获取锁成功
elsereturn 0 -- 返回0表示获取锁失败
end

使用方法:客户端通过 EVALEVALSHA 命令执行上述脚本,传递锁的键、值和过期时间。

3. Redisson 实现

Redisson 是一个高级的 Redis 客户端,它内置了对分布式锁的支持,提供了丰富的锁特性,如可重入、公平锁、锁自动续期等。

示例代码

Config config = new Config();
config.useSingleServer().setAddress("redis://localhost:6379");RedissonClient redisson = Redisson.create(config);
RLock lock = redisson.getLock("myLock");try {lock.lock(); // 可以传入等待时间和leaseTime等参数// 执行业务代码
} finally {lock.unlock();
}

Redisson 自动处理了锁的超时、重入和释放等问题,使得使用起来更加安全和便捷。

4. Redlock 算法

Redlock 算法由 Redis 的作者提出,旨在提供一个更安全的分布式锁实现。它要求客户端在多个独立的 Redis 节点上尝试获取锁,只有当大多数节点都同意时才认为获取成功。

步骤

  1. 客户端从 N 个独立的 Redis 节点中选出 N 个(通常 N >= 5),并尝试在每个节点上获取锁。
  2. 客户端计算获取锁成功与失败的比例,如果比例超过半数(且获取锁的节点数至少为 N/2 + 1),则认为成功获取锁。
  3. 如果获取锁成功,客户端计算所有锁的最小过期时间,并以此作为实际锁的有效时间。
  4. 在操作完成后,客户端向所有Redis实例释放锁。即使在某个节点上解锁失败,只要之前成功解锁了大多数节点,就可以认为锁已经被正确释放。

注意事项

  • 实际部署时,确保Redis实例分布在不同的物理机器上,减少共因失效的风险。
  • 需要权衡实例数量和性能、复杂性之间的关系,过多的实例会增加锁获取的复杂度和网络延迟。
  • 虽然Redlock在理论上很吸引人,但在某些情况下其安全性仍然存在争议,特别是关于网络分区的处理上。

五、Java+Redis分布式锁

在Java中使用Redis实现分布式锁,一个常见的做法是利用Jedis客户端直接操作Redis命令,或者使用客户端库如Redisson。

1. 基于Jedis的简单实现

首先,确保你的项目中已经添加了Jedis的依赖。

Maven依赖

<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.7.0</version>
</dependency>

Java代码示例

import redis.clients.jedis.Jedis;public class DistributedLockWithJedis {private Jedis jedis;private static final String LOCK_SUCCESS = "OK";private static final Long RELEASE_SUCCESS = 1L;public DistributedLockWithJedis(String host, int port) {jedis = new Jedis(host, port);}//使用 SET 命令的 NX 和 PX 来获取锁public boolean lock(String lockKey, String requestId, int expireTime) {String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime * 1000);return LOCK_SUCCESS.equals(result);}//使用Lua脚本来确保只有锁的持有者才能删除锁public boolean unlock(String lockKey, String requestId) {String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";Object result = jedis.eval(script, 1, lockKey, requestId);return RELEASE_SUCCESS.equals(result);}
}

lock方法尝试使用SET命令的NX(Only set the key if it does not already exist)和PX(Set the specified expire time, in milliseconds)选项来获取锁,同时设置锁的持有者和过期时间。unlock方法使用Lua脚本来确保只有锁的持有者才能删除锁,提高了操作的原子性。

2. 使用Redisson实现

Redisson是一个为Redis设计的Java客户端,提供了许多高级特性,包括分布式锁。

Maven依赖

<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.13.4</version>
</dependency>

Java代码示例

import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.config.Config;public class DistributedLockWithRedisson {private RedissonClient redisson;public DistributedLockWithRedisson(String address) {Config config = new Config();config.useSingleServer().setAddress(address);redisson = Redisson.create(config);}public void lockAndUnlock(String lockKey) {RLock lock = redisson.getLock(lockKey);try {lock.lock();// 执行业务代码System.out.println("Locked and processing...");} finally {lock.unlock();System.out.println("Unlocked.");}}
}

Redisson内部实现了锁的获取、续期、释放等方法,只需要调用简单的API即可。RLock接口提供了多种锁操作,包括公平锁、可重入锁等高级特性,极大地简化了分布式锁的使用。

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

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

相关文章

pikachu-Unsafe Filedownload

任意点击一个图片进行下载&#xff0c;发现下载的url。 http://127.0.0.1/pikachu/vul/unsafedownload/execdownload.php?filenamekb.png 构造payload&#xff1a; 即可下载 当前页面的源码&#xff0c;可以进行路径穿越来下载一些重要的配置文件来获取信息。 http://127.0.…

Android Audio基础——Track构造过程(十)

在 Android 中,Track 通常是指用于播放音频或视频的轨道或通道,用于音频流的控制。在这种语境下,AudioTrack 是用于播放音频的类之一,它提供了直接控制音频数据播放的功能。因此,可以说 AudioTrack 是 Android 中处理音频轨道的一种实现方式之一。 一、Track构造 我们接着…

函数对象引用题目

下列代码的输出是什么&#xff1f; python def func(x): return x1 funcs[func]*5 result[f(0) for f in funcs] print(result) A.[1,1,1,1,1] B.[1,2,3,4,5] C.[0,0,0,0,0] D.[0,1,2,3,4] 3 2 1 答案是A&#xff0c;你答对了吗&#xff1f; 解析&#xff1a; def fu…

前端基础入门三大核心之HTML篇 —— 同源策略的深度解析与安全实践

前端基础入门三大核心之HTML篇 —— 同源策略的深度解析与安全实践 一、同源策略&#xff1a;定义与起源1.1 定义浅析1.2 何为“源”&#xff1f;1.3 起源与意义 二、同源策略的运作机制2.1 限制范围2.2 安全边界 三、跨越同源的挑战与对策3.1 JSONP3.2 CORS3.3 postMessage 四…

VUE 创建组件常见的几种方式

在 Vue.js 中&#xff0c;组件的创建和使用通常遵循以下三种方法&#xff1a; 1. 全局组件 全局组件是通过 Vue.component() 方法创建的&#xff0c;注册后的组件可以在任何新创建的 Vue 实例&#xff08;包括根实例&#xff09;的模板中使用。 Vue.component(my-component,…

【MySQL精通之路】系统变量-持久化系统变量

MySQL服务器维护用于配置其操作的系统变量。 系统变量可以具有影响整个服务器操作的全局值&#xff0c;也可以具有影响当前会话的会话值&#xff0c;或者两者兼而有之。 许多系统变量是动态的&#xff0c;可以在运行时使用SET语句进行更改&#xff0c;以影响当前服务器实例的…

[AI Google] 10个即将到来的Android生态系统更新

新的体验带来了更强的防盗保护、手表电池寿命优化&#xff0c;以及对电视、汽车等的娱乐功能改进。 昨天&#xff0c;我们分享了Android如何以人工智能为核心重新构想智能手机。今天&#xff0c;我们推出了Android 15的第二个测试版&#xff0c;并分享了更多我们改进操作系统的…

java原型模式 (Prototype Pattern) 介绍

原型模式&#xff08;Prototype Pattern&#xff09;是一种创建型设计模式&#xff0c;它通过复制现有的实例来创建新对象&#xff0c;而不是通过实例化类来创建对象。这个模式允许你创建对象时避免复杂的初始化步骤&#xff0c;并且能够动态地创建对象的副本。 原型模式的关键…

Python导入Shapefile到PostGIS的常见问题和解决方案

导入Shapefile到PostGIS的常见问题和解决方案 先决条件&#xff1a; 已经拥有含有GDAL的python环境&#xff08;如果大家需要&#xff0c;我可以后面出一片文章 问题一&#xff1a;QGIS连接到PostGIS数据库失败 错误描述&#xff1a; Connection to server at &quo…

Vue3实战笔记(40)—组件逻辑复用:自定义Hooks的完全指南

文章目录 前言一、状态管理二、副作用处理三、 生命周期钩子总结 前言 自定义Hooks是Vue3中的一个重要特性&#xff0c;它允许您创建可重用的函数&#xff0c;以便在组件之间共享状态和逻辑。以下是一些关于自定义Hooks的常见用法。 一、状态管理 使用reactive或ref来创建响应…

题解:CF1969B(Shifts and Sorting)

题解&#xff1a;CF1969B&#xff08;Shifts and Sorting&#xff09; 一、题目翻译 给定一个二进制字符串&#xff0c;你可以将其中一个子段&#xff08;注意不是子串&#xff0c;是连续的&#xff09;进行循环移位——将最后一个字符放在第一个字符之前&#xff0c;其代价为…

2024 CCF国际AIOps挑战赛·赛题与赛制解读

本文根据本届挑战赛程序委员会主席、中国科学院计算机网络信息中心副研究员裴昶华在2024 CCF国际AIOps挑战赛线下宣讲会上为《2024 CCF国际AIOps挑战赛赛题与赛制》的分享整理而成&#xff0c;全文分为挑战赛背景介绍、题目简介、流程说明和评分规则等部分&#xff0c;最后简要…

别对我动心短视频:成都鼎茂宏升文化传媒公司

别对我动心短视频&#xff1a;时代的爱情哲学与心理探索 在短视频的海洋里&#xff0c;"别对我动心"这样的标题&#xff0c;如同一颗石子投入平静的湖面&#xff0c;激起了层层涟漪。它不仅仅是对一段情感的拒绝&#xff0c;更是一种现代人情感态度的表达&#xff0…

14 vue学习:透传Attributes

Attributes 继承 “透传 attribute”指的是传递给一个组件&#xff0c;却没有被该组件声明为 [props]或 [emits]的 attribute 或者 v-on 事件监听器。最常见的例子就是 class、style 和 id。 当一个组件以单个元素为根作渲染时&#xff0c;透传的 attribute 会自动被添加到根元…

【FAQ】HarmonyOS SDK 闭源开放能力 —Account Kit(2)

1.问题描述&#xff1a; 怎么判断登录的华为帐号有变动&#xff1f; 解决方案&#xff1a; 华为帐号登录成功后会返回唯一标识OpenID和UnionID&#xff0c;如果切换不同的华为帐号登录&#xff0c;这个唯一标识会变。 OpenID是华为帐号用户在不同类型的产品的身份ID&#x…

JWT Authorization header using the Bearer scheme

文章目录 引言I Json web token (JWT)1.1 JWT1.2 authentication_schemes(token authentication)1.3 SecurityScheme的实现II Swagger添加bearer token参数2.1 Swagger效果2.2 .net core Swagger实现Bearer Authentication2.3 java OpenAPI 3.0实现Bearer Authentication2.4 …

【全部更新完毕】2024长三角数学建模A题思路代码文章教学-“抢救”落水手机

文章摘要部分&#xff1a; “抢救”落水手机 摘要 文章主要探讨了如何科学地处理和搜索在水体中意外掉落的物品&#xff1a;华为 Mate 60 Pro手机和居民身份证。本文基于物理模型和动力学分析&#xff0c;为不同水体环境中的掉落物品提供了详尽的搜索策略和打捞建议。 本文…

[安洵杯 2019]crackMe

直接就退出程序了 找到关键函数了&#xff0c;好像用到了 hook 还有一个 嘿嘿&#xff0c;看着就是像 base64 只是 补‘’改成了‘ &#xff01; ’ 交叉引用啊&#xff0c;翻到一个应该是最后比较函数 1UTAOIkpyOSWGv/mOYFY4R!! 那一坨对 a1数组的操作没看懂 先总结一下就是…

SpringBoot搭建Eureka注册中心

系列文章目录 文章目录 系列文章目录前言前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 1、Spring-Cloud Euraka介绍 Spring-Cloud Euraka是Spring Cloud集合中一…

vue3 + vite 实用依赖与配置

mark一下日常 vue3 vite 项目配置 实用依赖与配置 1. amfe-flexible&#xff08;2&#xff09;安装&#xff08;3&#xff09;使用 2. postcss-pxtorem&#xff08;1&#xff09;介绍&#xff08;2&#xff09;安装&#xff08;3&#xff09;使用 3. autoprefixer&#xff08;…