Redis实现分布式锁可没有你想象的那么简单

从Xilinx FPGA到MIPI DSI LCD屏:手撸代码的实战经验分享 本文分享了使用Xilinx FPGA驱动MIPI DSI LCD屏的实战经验。作者从依赖官方IP核失败,到与底层D-PHY IP缠斗,最终通过手撸代码,直接配置GTY收发器原语并实现完整的D-PHY与DSI协议栈,成功点亮屏幕。文章详细剖析了协议细节、Vivado工具链的调试技巧与避坑指南,为FPGA工程师提供了从理论到实践的宝贵参考。 阅读详情

讲道理,这一篇不管是面试还是实际工作的试用都是非常有必要了解并且弄懂得,但是要看懂这篇的话需要的基础是懂好多我以前弄的一些知识点,如果好哥哥们在看的过程中有不熟悉的点可以看 Redis 高级进阶知识点大全,基本上 Reids 的东西都有了。觉得不错的话给猛男我点个赞加下关注啥的。

普通锁

首先锁是置于可启闭的器物上,以钥匙或暗码开启,像门锁、密码锁等。从我们日常生活中看,正常每个门都会有一把锁(不正常的就不要杠了),如果没有配置锁的话,什么样的人都可以进去(天下大同就是这种情况吧)。很显然这样是很不安全的,所以在这里引出了锁。

那在程序中锁是个什么样子呢?在程序中,锁一般出现在多线程的环境中。当多个线程访问同一个共享资源时,需要某种机制来保证只有满足某个条件(获取锁成功)的线程才能访问到资源,而不满足条件(获取锁失败)的线程只能等待,在下一轮竞争中来获取锁才能访问资源。所以锁是一种用来解决多个执行线程,访问共享资源时出现错误或数据不一致问题的工具。

实现方式

在这里需要注意的是,下面所有说的实现方式都是用Java实现,当然也不会对实现方式进行原理解析(后面可以弄)。

  1. 使用synchronized 关键字。synchronized是一个隐式锁,因为其解锁和锁定的操作是由 JVM 通过对象的 monitor 监视器锁自动完成的,我们也无法插手整个上锁和解锁的过程。
  2. 使用java.util.concurrent.locks.Lock的实现类,例如java.util.concurrent.locks.ReentrantLock。这种方式的话就是显示锁了,需要手动上锁和解锁。
  3. 使用CAS(Compare And Swap) 无锁机制,这个的话实际上属于乐观锁机制,核心概念就是比较并交换。在执行CAS操作的时候,将内存位置的值与预期原值比较,如果相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。

分布式锁

首先在分布式、微服务架构中,我们的项目通常是以下图模式来部署的,当然网关、nginx也都是存在集群的。

上图可以看到,变量A存在三个服务器内存中(这个变量A主要体现是在一个类中的一个成员变量,是一个有状态的对象)。假设我们使用的是普通锁,由于三台服务器之间变量互不可见,当多个请求调用时,可能就会出现操作三个不同内存区域的数据,读取的数据也可能会与设值得不一致。

基于以上问题,就出现了所谓的分布式锁(也不知道这个名字的作者是谁)的概念。其实简单点来说,就是在请求我们的接口时加上一个额外的步骤,只是这个步骤需要一个中间的媒介,比如像Redis、ZooKeeper 甚至使用Mysql。这个中间媒介的作用就是相当于一个标记,标记了当前是谁获取到了这个锁。整体的过程就是请求来了,先调用判断中间媒介是否存在,不存在则尝试获取,获取成功或者失败后则和普通锁处理逻辑一样。

实现方式

实现方式这一块上面也说了,主要需要一个中间的媒介。

  1. 基于Redis,不熟悉 Redis 的可以看 你不知道的 Redis 高级进阶知识点大全,也是今天的主题。
  2. 基于数据库,一般数据都有实现悲观锁,当然也可以用版本号或者其他乐观锁来实现,悲观锁的话主要还是 for update 关键字
  3. 基于Zookeeper,原理就是利用了Zookeeper的临时节点来实现。

推荐的话使用Zookeeper来实现分布式锁,主要是因为Zookeeper相关的特性非常适合用来做分布式锁,后续再来讲这一块的东西。当然,前提是项目中有使用到Zookeeper,不然就光为了实现一个分布式锁而搭建一套Zookeeper的集群那就得不偿失了。Redis则不一样,基本上的项目都会使用,当时使用Redis会有一些问题,所以才推荐使用Zookeeper。

Redis 实现

唯一性

使用 Redis 来实现锁的唯一性话主要是使用SETNX(SET if Not eXists)命令,这个命令的话就是当指定的 key 不存在时,为 key 设置指定的值。设置成功则返回 1 。 设置失败则返回 0。

哎,这固然解决了上面的说的做个标记,当多个请求来时只有一个能通过SETNX(SET if Not eXists)设置成功。但是的但是,如果这个请求报错了、死循环了或者整个项目直接宕掉了,那后面的所有线程是不是都不能设置成功了呢? 这里的话就引出了第二个问题点,怎么防止死锁。

锁超时

在 Redis 中防止死锁可以使用设置key的超时时间。讲道理,这个超时时间是必须要设置的,Redis 也提供了expire 命令来对key设置过期时间。那这样是不是就可以解决了呢?

答案是否定的,为什么呢?如果好哥哥们有看过我的关于 Redis 系列的文章都知道一个点,那就是 Reids 是单线程模型,关于这个这里的话就不详细说了,可以看 你不知道的 Redis 高级进阶知识点大全。这篇里面有个目录,翻一翻里面的文章就知道了。由于这个模型所以导致了 Redis 整个命令的执行都是要放到队列里面去的,然后依次排队执行。这也就是说当执行setnx和expire时,由于是两条命令,所以会有两次请求与响应的步骤,这个时候就会产生原子性的问题。比如设置完setnx后程序直接宕掉,这是不是又回到了上一个问题。那怎么解决两条命令的原则性问题呢? 好哥哥们往下看。

命令原子性

解决上述问题最简单的办法那就是将两条命令合成一条,那 Redis 支持这种模式吗?答案是肯定的,我们可以使用 Pipeline、 Redis 事务 和 Lua 脚本这三种方式来实现。但是Pipeline 和 Redis 事务都是不具备原子性的(这个点文章进去看看明白了),所以我们只能使用Lua 脚本了。有不熟悉 Redis 怎么使用Lua 脚本的建议先看完上面的文章。

这里贴一个加锁的Lua,这里实际上是一个字符串。

"if (redis.call('exists', KEYS[1]) == 0) then " +
                        "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
                        "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                        "return nil; " +
                        "end; " +
                        "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
                        "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
                        "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                        "return nil; " +
                        "end; " +
                        "return redis.call('pttl', KEYS[1]);"

总的来说就是使用Lua 脚本来保证多条命令的原子性问题。那上面死锁的解决了,是不是就完美了呢?并没有想象的那么完美,好哥哥们想这么一个问题,假如给锁加的超时时间是5秒钟,但是我们在做逻辑操作时,可能因为需要调用别人的接口或者查询数据库出现慢查询超过了5秒,正常逻辑是当前线程还需要持有这把锁。然后这时锁过期失效了,其他线程就能竞争获取锁,是不是又出现了另外一个线程不安全的问题?

Redisson 原理解析

要解决上面逻辑还未执行完而锁已经过期失效的问题需要借助Redisson这个框架来解决,实际上 Redis 并没有对这一块提供相关的支持,因为做不到。只能通过应用程序编写对应的业务逻辑来实现,像上面锁超时、唯一性和原子性的问题,Redisson已经有整合过了。在我们平常选择用 Redis 来做分布式锁的解决方案的话建议还是使用这个框架,不然的话需要考虑很多分布式环境并发下的问题。

原理

整个Redisson加锁的过程如下:

整个流程就不用文字来表达了,基本上看图就能明白了。这里的话列举一下需要注意的点,如下:

  1. 加锁机制浴巾上面提到的SETNX指令和Lua脚本来实现
  2. watch dog自动延期机制,就是会启动一个后台线程,定时比如说10秒检测当前线程是否还持有了锁,是的话就延期。这个的话就是解决上面逻辑还未执行完而锁已经过期失效的问题。但是是不建议开启的,这个还是很耗资源和性能的。
  3. 可重入性的话Redisson存储锁的数据类型是 Hash 类型,并且Hash数据类型的key值包含了当前线程信息。
  4. 互斥性是通过 Redis 数据结构来保证分布式锁的唯一性。
  5. 避免死锁是通过 Redis 对key设置超时时间来保证。

存在的问题

是的,到这里还没有完。通过Redisson是实现分布式锁依然会存在单点/多点,这里主要是关于部署方式上。使用单点的话 Redis 宕机了,那就没什么说的了,直接加不了锁咯,这个不管用哪个中间媒介都解决不了。

多点的话主要是哨兵和Cluster集群(不熟悉的也可以从 Redis 高级进阶知识点大全 中找找)。假设客户端 1 向某个master节点写入了Redisson锁,此时会异步复制给对应的 slave节点。但是这个过程中一旦发生 master节点宕机,主备切换,slave节点从变为了 master节点。这时客户端 2 来尝试加锁的时候,由于客户端 1 已经宕机的master节点加锁成功,而客户端 2 在新的master节点上也能加锁,此时就会导致多个客户端对同一个分布式锁完成了加锁。

解决方案

  1. 不解决,不要打我(手动狗头),解决不了我就加入他。这个还真的不好弄,目前没有想到合适的方式来处理。
  2. 用Zookeeper实现,抛弃它(使用这种方式的都是渣男)。这也是我推荐使用Zookeeper来实现分布式锁的原因,好哥哥们是不是也发现了,使用 Redis 来实现分布式锁是真的复杂,各类问题太多了。

应用场景

  1. 避免不同节点重复的执行某一块逻辑,比如说我们的定时任务,不加锁的情况下在某一时间点这个定时任务可能会执行多次。
  2. 防止表单重复提交,这个其实也是会有问题。
  3. 避免破坏数据的正确性,当多个线程同时某一块逻辑时,可能会出现数据的错乱或者不一致。

 

 

储能电池管理系统介绍(一) 介绍储能BMS的架构以及各模块功能 阅读详情

相关推荐

6、基于集成学习技术的边坡稳定性预测及滑坡易发性研究

本研究基于集成学习技术对云阳县的边坡稳定性及滑坡易发性进行预测分析。通过比较SVM、RF、XGBoost和LR四种模型,发现集成学习模型在准确率和少数类识别方面表现更优。特征重要性分析表明剖面形状是影响边坡稳定性的关键因素。同时,引入基于定性分析的分区建模方法,构建父模型与子区域模型,提升了滑坡易发性制图的精度。研究成果为地质灾害的预防与管理提供了科学依据和技术支持。

peach的博客 116

Redis实现分布式锁的7种方案

分布式环境下,可以把键设置为的名称,把值设置为当前进程的唯一标识(例如IP地址或进程ID),这样多个进程同时尝试设置同一个键时,只有一个能够成功设置成,表示该进程获得了。RedSync的具体实现方式可以参考https://github.com/redisson/redisson/wiki/8.-%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81。该方法的缺点是无法解决多进程操作的原子性问题,即在获取和设置过期时间这两个步骤中间可能会出现错误的情况,导致的状态出现问题。

wgq2020的博客 731

开源的预测气象大模型与数据获取/可视化

「开源气象大模型与数据获取指南」摘要: 当前主流AI气象模型(如ECMWF的AIFS、DeepMind的GraphCast、华为Pangu等)主要基于ERA5/HRES数据训练。开源资源可通过ECMWF OpenData、GitHub仓库及Google Cloud获取,其中AIFS数据采用CCBY-4.0许可可商用,其他模型多有限制性许可。典型应用流程包括:1)通过Python包获取实时预报数据;2)加载预训练权重进行推理;3)使用xarray/cartopy或Zarr进行可视化。训练数据可从CDS下载ER

专注AI大模型,软件混淆,授权 716

详解 Redis 分布式锁的 5 种方案

本地加的方式在分布式的场景下不适用,所以本文我们来探讨下如何引入分布式锁解决本地的问题。本篇所有代码和业务基于我的开源项目 PassJava。本篇主要内容如下:一、本地的问题首先我们来回顾下本地的问题:目前题目微服务被拆分成了四个微服务。前端请求进来时,会被转发到不同的微服务。假如前端接收了 10 W 个请求,每个微服务接收 2.5 W 个请求,假如缓存失效了,每个微服务在访问数据库时加...

Java知音 1929

redis的用法

互斥性。在任意时刻,只有一个客户端能持有。不会发生死。即使有一个客户端在持有的期间崩溃而没有主动解,也能保证后续其他客户端能加。解铃还须系铃人。加和解必须是同一个客户端,客户端自己不能把别人加的给解了。加和解必须具有原子性。

weixin_51725685的博客 1232

Redis 实现分布式锁没有想象的那么简单

前言 上篇又说到就是最近不写 Redis 系列相关的了,但是由于在纠结选择Sping或者Netty。一时间就想起来还有这一篇相关的东西没有弄,也当成是一个过渡期。讲道理,这一篇不管是面试还是实际工作的试用都是非常有必要了解并且弄懂得,但是要看懂这篇的话需要的基础是懂好多我以前弄的一些知识点,如果好哥哥们在看的过程中有不熟悉的点可以看 Redis 高级进阶知识点大全,基本上 Reids 的东西都有了。觉得不错的话给猛男我点个赞加下关注啥的。 普通 首先是置于可启闭的器物上,以钥匙或暗码开启,像门、密码

AntizZ 的博客 350

RedisRedis分布式锁

* 极端情况** 线程1现在持有之后,在执行业务逻辑过程中,他正准备删除,而且已经走到了条件判断的过程中,比如他已经拿到了当前这把确实是属于他自己的,正准备删除,但是此时他的到期了,那么此时线程2进来,但是线程1他会接着往后执行,当他卡顿结束后,他直接就会执行删除那行代码,相当于条件判断并没有起到作用,这就是删时的原子性问题,之所以有这个问题,是因为线程1的拿,比,删,实际上并不是原子性的,我们要防止刚才的情况发生,乐观适合更新数据,而插入数据适合使用悲观

George_huhu的博客 1152

Redis分布式锁原理与实现

分布式系统中,数据的一致性和并发控制是至关重要的。随着微服务架构的普及,多个服务实例可能同时访问同一份数据,这就需要一种机制来确保数据的一致性和避免并发冲突。Redis分布式锁正是为了解决这一问题而诞生的。想象一个在线支付系统,当用户发起支付请求时,系统需要确保同一时间只有一个服务实例处理这笔交易,以防止重复扣款或数据不一致。这种情况下,分布式锁就扮演了至关重要的角色。Redis分布式锁是一种基于Redis机制,它利用Redis的原子操作来保证的互斥性和可见性。

我是Java程序员廖志伟,感谢朋友们的支持! 1923

Redis String 实现分布式锁

分布式系统中,并发控制就像是多个人同时进出一个房间,如何确保秩序?想象一下这个场景:双十一抢购活动中,某个热门商品只剩最后 10 件,此时有几万人同时点击"下单"。如果没有适当的并发控制,很可能导致最终售出 100 件(严重超卖),但实际仓库只有 10 件商品,这将带来糟糕的用户体验和业务混乱。在单机应用中,我们可以使用 Java 的关键字或轻松解决这类问题。但在分布式环境下,这些本地无法跨服务器工作,此时我们需要一种能在多个服务实例间协调的机制——分布式锁

m0_74109105的博客 1080

Redis中的分布式锁之SETNX底层实现

Redis分布式锁SETNX实现原理详解 本文深入分析了Redis中SETNX命令实现分布式锁的核心机制。首先通过餐厅点菜的类比场景,形象说明了分布式锁的必要性。随后详细解析了SETNX的执行流程,包括原子性检查键是否存在和设置键值的过程,以及配合EXPIRE命令防止死的安全实践。 文章重点剖析了SETNX的底层技术原理,包括Redis字典数据结构、渐进式rehash机制和内存管理策略。通过源码片段展示了Redis处理SETNX命令的核心逻辑,并强调分布式锁实现中的关键安全考量因素。 最后总结了SETNX

从不水文,坚持创作,加油 1121

Go语言实现Redis分布式锁

本文将基于go语言,使用了一个常用的go Redis客户端一步一步探索与实现一个简单Redis分布式锁。SETNX 命令用于在Redis中设置某个不存在的键的值。如果该键不存在,则设置成功,如果该键存在,则设置失败,不作任何动作。基于此可以实现一种简单的抢占机制。先连接redis。新建lock.go文件。创建lock结构体,加和解方法。

m0_57408211的博客 3341

基于RedisRedisson实现分布式锁

分布式锁介绍 在计算机系统中,作为一种控制并发的机制无处不在,单机环境下,操作系统能够在进程或线程之间通过本地的来控制并发程序的行为。而在如今的大型复杂系统中,通常采用的是分布式架构提供服务。分布式环境下,基于本地单机的无法控制分布式系统中分开部署客户端的并发行为,此时分布式锁就应运而生了。 一个可靠的分布式锁应该具有以下特征: ①互斥:作为,需要保证任何时候只能有一个客户端持有。 ②可重入:同一个客户端在获得后,可以再次进行加。 ③高可用:获取和释放的效率较高,不会出现单点故

猫咪老师 3221

Redis原理篇——分布式锁

简要介绍了基于 redis 实现分布式的相关原理问题

祝你身体健康,美满幸福 2223

Redis分布式锁Redission底层实现

Redis分布式锁Redission实现原理与应用 摘要:本文深入分析了Redis分布式锁Redission客户端的底层实现机制。Redission通过SET NX PX命令获取,并引入看门狗机制定期续期,确保的可靠性。其核心实现包含Lua脚本保证原子性、可重入设计以及完善的异常处理。与简单SETNX相比,Redission提供了更完备的分布式锁功能,包括等待、自动续期和释放通知等特性。文章详细介绍了Redission分布式锁的使用方法、技术原理和最佳实践,同时对比了与其他分布式锁方案的差异。R

从不水文,坚持创作,加油 1331

redis分布式锁可能不那么简单

在计算机世界里,对于大家并不陌生,在现代所有的语言中几乎都提供了语言级别实现,为什么我们的程序有时候会这么依赖呢?这个问题还是要从计算机的发展说起,随着计算机硬件的不断升级,多核cpu,多线程,多通道等技术把计算机的计算速度大幅度提升,原来同一时间只能执行一条cpu指令的时代已经过去。随着多条cpu指令可以并行执行的原因,原来不曾出现的资源竞争随着出现,在程序中的体现就是随处可见的多线程...

架构师修行之路 4221

Redis分布式锁实现原理与实战案例分析

分布式系统的世界里,多个服务实例同时运行时,如何协调它们对共享资源的访问成为一个棘手的问题。想象一下,如果多个快递员同时试图将包裹放入你家的信箱,没有协调机制的话,可能会导致包裹丢失或放错位置—这就是分布式系统面临的并发挑战。分布式系统并发挑战:为解决这些问题,分布式锁应运而生。而Redis作为分布式锁实现方案,就像一个轻巧而高效的交通信号灯,以其独特的优势成为许多开发者的首选。为什么选择Redis实现分布式锁?与其他分布式锁方案相比,Redis展现出明显的优势和特点

sinat_27016095的博客 1965

Redis如何实现一个分布式锁?

Redis分布式锁通过原子性操作、过期时间和唯一标识实现互斥访问共享资源。核心流程:1)使用SET命令(NX+EX参数)原子性获取并设置过期时间;2)业务逻辑执行完毕时,通过Lua脚本验证唯一标识后删除。该方案解决了传统方案的三个问题:1)SETNX+EXPIRE非原子性导致的死;2)未及时释放引发的死;3)误删问题。通过给每个设置唯一值,确保只有持有者才能释放,同时过期时间机制避免了客户端崩溃导致的死问题。

Jackasher的博客 1123

Unity AB包加密与解密

运用AES加密算法对Unity的AB包部分内容进行加密以保护资源安全,选取文件中的多段字节以及固定长度进行加密,避免了加密AB包的全部内容所带来的多余性能消耗。

crestron simpl windows V4.2;2022年最新版 快思聪编程软件 simpl_windows

crestron simpl_windows_4.2000.00.00 快思聪编程软件

上一篇: Java并发:线程间的通信
下一篇: 记一次工作中微服务项目迁移拆分的过程
头顶假发
博客等级 码龄4年 92粉丝 · 574原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值