Redis实现方式开启新篇章,解决分布式环境下的资源竞争问题,提升系统稳定性

news2024/11/22 13:47:20

前言

分布式锁一般有三种实现方式:

  1. 数据库乐观锁;
  2. 基于Redis的分布式锁;
  3. 基于ZooKeeper的分布式锁

本篇博客将介绍第二种方式,基于Redis实现分布式锁。

虽然网上已经有各种介绍Redis分布式锁实现的博客,然而他们的实现却有着各种各样的问题,为了避免误人子弟,本篇博客将详细介绍如何正确地实现Redis分布式锁。

可靠性

首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:

  • 互斥性。在任意时刻,只有一个客户端能持有锁。
  • 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。
  • 具有容错性。只要大部分的Redis节点正常运行,客户端就可以加锁和解锁。
  • 解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。

代码实现

组件依赖

首先我们要通过Maven引入Jedis开源组件,在pom.xml文件加入下面的代码:

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

加锁代码

正确姿势

先展示代码,再带大家慢慢解释为什么这样实现:

f9299160dd1487b3b09646d62320b3ef.jpeg

可以看到,我们加锁就一行代码:

jedis.set(String key, String value, String nxxx, String expx, int time)

这个set()方法一共有五个形参:

第一个为key,我们使用key来当锁,因为key是唯一的。

第二个为value,我们传的是requestId,很多童鞋可能不明白,有key作为锁不就够了吗,为什么还要用到value?

原因就是我们在上面讲到可靠性时,分布式锁要满足第四个条件解铃还须系铃人,通过给value赋值为requestId,我们就知道这把锁是哪个请求加的了,在解锁的时候就可以有依据。

requestId可以使用UUID.randomUUID().toString()方法生成。

第三个为nxxx,这个参数我们填的是NX,意思是SET IF NOT EXIST,即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作;

第四个为expx,这个参数我们传的是PX,意思是我们要给这个key加一个过期的设置,具体时间由第五个参数决定。

第五个为time,与第四个参数相呼应,代表key的过期时间。

总的来说,执行上面的set()方法就只会导致两种结果:

  1. 当前没有锁(key不存在),那么就进行加锁操作,并对锁设置个有效期,同时value表示加锁的客户端。
  2. 已有锁存在,不做任何操作。

心细的童鞋就会发现了,我们的加锁代码满足我们可靠性里描述的三个条件。

首先,set()加入了NX参数,可以保证如果已有key存在,则函数不会调用成功,也就是只有一个客户端能持有锁,满足互斥性。

其次,由于我们对锁设置了过期时间,即使锁的持有者后续发生崩溃而没有解锁,锁也会因为到了过期时间而自动解锁(即key被删除),不会发生死锁。

最后,因为我们将value赋值为requestId,代表加锁的客户端请求标识,那么在客户端在解锁的时候就可以进行校验是否是同一个客户端。

由于我们只考虑Redis单机部署的场景,所以容错性我们暂不考虑。

错误示例1

比较常见的错误示例就是使用jedis.setnx()和jedis.expire()组合实现加锁

代码如下:

f6f96af2419236e56fba95d8b9621426.jpeg

setnx()方法作用就是SET IF NOT EXIST,expire()方法就是给锁加一个过期时间。

乍一看好像和前面的set()方法结果一样,然而由于这是两条Redis命令,不具有原子性,如果程序在执行完setnx()之后突然崩溃,导致锁没有设置过期时间。那么将会发生死锁。

网上之所以有人这样实现,是因为低版本的jedis并不支持多参数的set()方法。

错误示例2

这一种错误示例就比较难以发现问题,而且实现也比较复杂。实现思路:使用jedis.setnx()命令实现加锁,其中key是锁,value是锁的过期时间。

执行过程:

  1. 通过setnx()方法尝试加锁,如果当前锁不存在,返回加锁成功。
  2. 如果锁已经存在则获取锁的过期时间,和当前时间比较,如果锁已经过期,则设置新的过期时间,返回加锁成功。

代码如下:

537c0392094e02b77ac20eff3e5edce5.jpeg

那么这段代码问题在哪里?

1. 由于是客户端自己生成过期时间,所以需要强制要求分布式下每个客户端的时间必须同步。

2. 当锁过期的时候,如果多个客户端同时执行jedis.getSet()方法,那么虽然最终只有一个客户端可以加锁,但这个客户端的锁过期时间可能被其他客户端覆盖。

3. 锁不具备拥有者标识,即任何客户端都可以解锁。

解锁代码

正确姿势

还是先展示代码,再带大家慢慢解释为什么这样实现:

fc3f6c97d8f7a64f0d00c0860bd076d7.jpeg

可以看到,我们解锁只需要两行代码就搞定了!

第一行代码,我们写了一个简单的Lua脚本代码,上一次见到这个编程语言还是在《黑客与画家》里,没想到这次居然用上了。

第二行代码,我们将Lua代码传到jedis.eval()方法里,并使参数KEYS[1]赋值为lockKey,ARGV[1]赋值为requestId。eval()方法是将Lua代码交给Redis服务端执行。

那么这段Lua代码的功能是什么呢?

其实很简单,首先获取锁对应的value值,检查是否与requestId相等,如果相等则删除锁(解锁)。

那么为什么要使用Lua语言来实现呢?

因为要确保上述操作是原子性的。关于非原子性会带来什么问题,可以阅读【解锁代码-错误示例2】

那么为什么执行eval()方法可以确保原子性,源于Redis的特性,下面是官网对eval命令的部分解释:

简单来说,就是在eval命令执行Lua代码的时候,Lua代码将被当成一个命令去执行,并且直到eval命令执行完成,Redis才会执行其他命令。

错误示例1

最常见的解锁代码就是直接使用jedis.del()方法删除锁,这种不先判断锁的拥有者而直接解锁的方式,会导致任何客户端都可以随时进行解锁,即使这把锁不是它的。

public static void wrongReleaseLock1(Jedis jedis, String lockKey) {
jedis.del(lockKey);
}

错误示例2

这种解锁代码乍一看也是没问题,甚至我之前也差点这样实现,与正确姿势差不多,唯一区别的是分成两条命令去执行

代码如下:

c5c5ec5f7ac013fb508baa2a2c08b9c9.jpeg

如代码注释,问题在于如果调用jedis.del()方法的时候,这把锁已经不属于当前客户端的时候会解除他人加的锁。

那么是否真的有这种场景?

答案是肯定的,比如客户端A加锁,一段时间之后客户端A解锁,在执行jedis.del()之前,锁突然过期了,此时客户端B尝试加锁成功,然后客户端A再执行del()方法,则将客户端B的锁给解除了。

总结

本文主要介绍了如何使用Java代码正确实现Redis分布式锁,对于加锁和解锁也分别给出了两个比较经典的错误示例。

其实想要通过Redis实现分布式锁并不难,只要保证能满足可靠性里的四个条件。

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

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

相关文章

万字长文搞懂产品模式和项目模式

项目模式是很多传统企业对IT进行投资的方式&#xff0c;相对于产品模式&#xff0c;项目模式在团队建设、快速反应等方面存在很多弊端&#xff0c;本文从多个角度比较了产品和项目两种团队模式的利弊&#xff0c;并在最后回答了若干常见问题。原文: Products Over Projects 软件…

活动回顾|KCD 2023 杭州站

KCD 2023 活动介绍 HANGZHOU 关于 KCD Kubernetes Community Days&#xff08;KCD&#xff09;由云原生计算基金会&#xff08;CNCF&#xff09;发起&#xff0c;由全球各国当地的 CNCF 大使、CNCF 员工以及 CNCF 会员单位联合组织。目前 KCD 正在全球各个国家活跃地组织进行中…

nvidia-smi

1、说明 nvidia-smi 是 NVIDIA System Management Interface 的缩写&#xff0c;是 NVIDIA 提供的一个命令行工具&#xff0c;用于查看和管理 NVIDIA GPU 设备的信息。执行这个命令通常可以获取关于系统上安装的 NVIDIA GPU 的实时状态和性能信息。 一般来说&#xff0c;nvid…

内裤洗衣机有用吗?公认好用的四大款内衣洗衣机推荐

内衣目前洗衣机已经成为我们生活中不可缺少的一种电器。这不但可以节约我们的时间&#xff0c;而且能更好地清洗干净贴身衣物&#xff0c;使我们的生活变得更健康。那么到底哪些内衣洗衣机值得买呢&#xff1f;哪些内衣洗衣机清洗效果会更干净呢&#xff1f;这次&#xff0c;我…

众和策略:谷歌大跌!土耳其股市,两度熔断!

当地时间周三&#xff0c;美股三大指数团体收跌&#xff0c;谷歌等大型科技股连累纳指大跌超2%&#xff0c;创2月以来最大单日跌幅。 当日&#xff0c;谷歌股价大跌近10%至三个月低位&#xff0c;市值一夜蒸发超1600亿美元。周二盘后&#xff0c;谷歌母公司Alphabet发布了最新…

FinalShell或者XShell工具 突然连不上服务器(绝对好使!)

关闭网络管理器 systemctl stop NetworkManager禁止网络管理器开机重启 systemctl disable NetworkManager重启网络 service network restart绝对好使&#xff01;

技术实力获行业高度认可,几何伙伴首获科技进步奖殊荣!

2023年10月25日&#xff0c;“2023年度中国汽车工程学会科学技术奖”正式公布并于北京举行颁奖典礼&#xff0c;以对汽车工业努力奋斗、勇于创新的优秀企业和人才进行嘉奖&#xff0c;对优秀先进成果进行表彰。 其中&#xff0c;由几何伙伴主导&#xff0c;携手上汽集团、友道智…

睿趣科技:抖音小店在哪里选品

随着抖音平台的日益火爆&#xff0c;越来越多的商家选择在抖音小店开设自己的店铺。然而&#xff0c;对于许多新手来说&#xff0c;如何选品却成为了一个难题。那么&#xff0c;抖音小店应该在哪里选品呢? 首先&#xff0c;我们可以从抖音平台上的热门商品入手。通过观察抖音上…

创业酵母:为创业酵母的新增长,数字化“发酵”

成立于2014年11月的创业酵母是一家以教育和咨询为核心的一站式企业服务平台。 张丽俊&#xff08;Cherry&#xff09;和俞朝翎&#xff08;俞头&#xff09;是创业酵母的两位创始人&#xff0c;由于两位创始人都是第一代阿里人&#xff0c;他们希望能将自己在阿里沉淀下来的经验…

NPDP产品经理证书是什么行业的证书?

NPDP是一个跨行业的证书&#xff0c;它适用于各种不同类型和规模的组织。无论是制造业、服务业还是科技领域&#xff0c;都可以从NPDP认证中获益。 1. 制造业&#xff1a; 制造业涉及大量的产品开发和创新活动。从汽车制造到电子设备制造&#xff0c;从家居用品到航天航空&…

重复性管理--抽象的重要性(上)

Haskell 语言的设计者之一 Paul Hudak 曾说过一句略带夸张的话(overstatement): 编程中最重要的三件事是: 抽象, 抽象, 抽象. “abstraction, abstraction, abstraction” are the three most important things in programming. 如果你去问一些资深开发者, 程序员最重要的的能力…

电商API接口教程|设计一个牛逼的Java API接口?

主要从以上三个方面来设计一个安全的电商API接口&#xff08;包含淘宝/天猫/京东/拼多多/抖音/跨境电商亚马逊/阿里巴巴/LAZADA等&#xff09;。 一 安全性问题 安全性问题是一个接口必须要保证的规范。如果接口保证不了安全性&#xff0c;那么你的接口相当于直接暴露在公网环…

vue3+klinecharts实现k线(附完整代码)

vue3klinecharts实现k线 环境安装图表配置创建一个容器实现点击时间切换图表完整代码 环境 vue3 klinecharts 最终实现 安装 npm install klinecharts图表配置 具体可用参考官网 https://klinecharts.com/guide/styles.html const option {crosshair: {show: true,//…

操作系统进程调度算法的模拟实现(c语言版本)

前言&#xff1a;本文旨在分享如何使用c语言对操作系统中的部分进程调度算法进行模拟实现&#xff0c;以及算法描述的讲解&#xff0c;完整代码放在文章末尾&#xff0c;欢迎大家自行拷贝调用 目录 常见的调度算法 数据结构 先来先服务调度算法 算法模拟思路&#xff1a; …

LeetCode刷题---简单组(三)

文章目录 &#x1f352;题目一 20. 有效的括号&#x1f352;解法一&#x1f352;题目二 26. 删除有序数组中的重复项&#x1f352;解法一&#x1f352;题目三 21. 合并两个有序链表&#x1f352;解法一 &#x1f352;题目一 20. 有效的括号 给定一个只包括 ‘(’&#xff0c;‘…

Q-learning智能体

Q-learning智能体 Q-learning算法是一种无模型、在线、非策略的强化学习方法。Q-learning算法是一种基于价值的强化学习算法&#xff0c;它训练一个评价网络去估计回报或未来奖励。对于给定的观测值&#xff0c;智能体选择并输出估计收益最大的动作。 注&#xff1a;Q-learnin…

PL/SQL工具下载地址

https://www.allroundautomations.com/registered-plsqldev/ 选择需要下载的版本即可

Shopee、Lazada卖家不得不看的提升销量技巧,自养号测评打造权重

近年来&#xff0c;大部分虾皮、Lazada卖家开始通过测评补单的方式来提升店铺权重和产品排名&#xff0c;以吸引更多流量。这种方式可以有效提高产品的销售转化率&#xff0c;对店铺的运营起到推动作用。然而&#xff0c;测评补单并非简单的购买过程&#xff0c;其中涉及到许多…

网页提示证书错误 怎么办

随着网站使用SSL证书来加密网站信息&#xff0c;SSL证书的应用也越来越广泛&#xff0c;那么有时候我们进入网站&#xff0c;明明安装了SSL证书&#xff0c;为什么也会提示证书错误呢&#xff1f; SSL证书错误可能是由以下原因导致的&#xff1a; 1. 证书过期&#xff1a; SSL证…

推荐一个假数据接口网页 适用于示例项目

DummyJSON - Fake REST API of JSON data for development