关于分布式锁你需要知道的事

Redisson的看门狗策略——保障Redis数据安全与稳定的机制 看门狗策略是一种自动检测并处理过期键的机制。它基于Redis的“WATCH”命令实现,通过在Redisson库中创建一个监视器(Watch Dog)来监控Redis服务器上的指定键。当应用程序使用Redisson库监视一个键时,Watch Dog会向Redis服务器发送一个“WATCH”命令,并在服务器上对该键进行监视。如果另一个客户端尝试修改被监视的键,Redis服务器将返回一个错误。这个错误会被Watch Dog捕获并处理。 阅读详情

什么是分布式锁


通过互斥性质,来保证线程对分布式系统中共享资源的有序访问

说人话:一把锁,挨个进

分布式锁的特性


  • 互斥(线程独享):即同一时刻只有一个线程能够获取锁
  • 避免死锁:获得锁的线程崩溃后,不会影响后续线程获取锁,操作共享资源
  • 隔离性:A获取的锁,不能让B去解锁(解铃还须系铃人)
  • 原子性:加锁和解锁必须保证为原子操作

分布式锁的实现方式


  1. 基于Redis
  2. 演变过程:

V-1.0:

SETNX:Redis提供了SETNE(SET if Not eXists)命令,表示当Key不存在时,才能设置Value,否则设置失败(获取锁失败)

DEL KEY:第一步获取锁成功,对共享资源操作完后,释放锁

问题:如果业务代码出现异常,阻塞或者报错了,那么该线程就一直持有锁,不释放,其他线程也永远获取不到

V-2.0:

SETNX+EXPIRE:给锁上过期时间,假如持有锁线程崩溃了,达到设置的过期时间后,会自动释放锁,避免后续线程获取不到锁!

问题:仍旧会死锁!SETNX和EXPIRE是两条命令,Redis单命令是原子操作,但多条命令为非原子操作!SETNX执行成功,EXPIRE失败时就会发生死锁

v-3.0:

SET(NX+EX)(2.6.12版本之后):获取锁,并设置锁过期时间(原子操作)

如此,可以说是彻底解决了死锁问题!


那么还问存在其他问题吗?
分析分布式锁的特征:互斥、死锁、原子等特性,我们都算是解决了!
但还未考虑隔离性的问题!


场景

  1. 线程A加锁成功后,去操作共享资源
  2. 但是因为发生了意外,线程A操作的时间超过了锁过期时间,锁被释放了
  3. 线程B进来了,枷锁成功,去操作共享资源了
  4. 此时,线程A操作完成了,回来释放锁,线程B的锁被A释放
    隔离性带来的问题:

  1. 锁的过期时间设置不合理,导致线程A锁过期,被释放
  2. 线程A释放了线程B的锁

分析:


  • 线程A的过期时间设置不合理,那就换一个合理的时间————对应到现实工作中,就是根据程序员的工作经验,对改值进行较为合理的设置
  • 其实很简单,锁过期就像去麦当劳喝咖啡喝完了呗,还想喝怎么办?续杯!————获取锁时,先设置一个过期时间,同时,开启一个守护线程,定时去查看锁的剩余存活时间,假如锁的存活时间快过期了,但业务代码还没执行完,赶紧去给大爷续杯,即重新设置过期时间(看门狗)
  • 至于第二个问题,还是那句老话————解铃还须系铃人,加一个业务唯一标识,每个线程只能根据业务唯一去释放自己的锁,同时,需要注意:判断是否为自己的锁和删除锁应为原子操作!不然仍旧会删错锁!


实现


  • Redission的看门狗(基于Netty时间轮算法实现):
private long lockWatchdogTimeout = 30 * 1000;public RedissonLock(CommandAsyncExecutor commandExecutor, String name) {    super(commandExecutor, name);    this.commandExecutor = commandExecutor;    //会获取看门狗设置的时间,默认为10s检查一次,锁过快过期,且业务代码还没执行完,就会给锁续上这个时间,默认30s    this.internalLockLeaseTime = commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout();    this.pubSub = commandExecutor.getConnectionManager().getSubscribeService().getLockPubSub();}private RFuture<Boolean> tryAcquireOnceAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {    //如果锁是永不过期,那么就按常规方式索取锁    if (leaseTime != -1) {        return tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_NULL_BOOLEAN);    }    //否则,会在获取锁之后,加一个定时任务,在锁执行完业务代码自行释放之前,不断的给所续上过期时间(默认10s检查一次,每次给锁续期30s)    RFuture<Boolean> ttlRemainingFuture = tryLockInnerAsync(waitTime, internalLockLeaseTime,TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_NULL_BOOLEAN);    ttlRemainingFuture.onComplete((ttlRemaining, e) -> {        if (e != null) {            return;        }        // lock acquired        if (ttlRemaining) {            scheduleExpirationRenewal(threadId);        }    });    return ttlRemainingFuture;}

实现具体细节,参见Redission源码


  • 线程隔离的问题:

考虑到获取锁判断后,再删除锁,这两个操作必须是原子性的,那么就需要查看一下Redis的API有没有提供这两个操作的原子性操作了
结果发现,没有!那么叫考虑第二种方案,在Redis中除了单条命令是原子性的,还有执行Lua脚本也是原子性操作!


//如果是自己的锁,则进行删除,否则返回if redis.call("GET",KEY[1]) == ARGV[1] then             return redis.call("DEL",KEY[1])else  return 0end

总览


小总结


经过了这几波优化之后,基于Redis的分布式锁(Redis单实例),可算是安全放心大胆的使用了!


作为技术宅男,要有极客精神(,有心的人,可能会发现,以上分析的分布式锁适合单节点的Redis实例,如果遇到主从+哨兵的模式基本凉凉!

凉凉场景:

  1. 线程A在遇到主从架构时,先在Master上加锁成功
  2. 此时,还未等加锁命令SET同步到Slave上,Master就出现问题,宕机了!
  3. 通过哨兵过半原则,重新选出新的主节点,那么此时这把锁在新的主库上是找不到的!出现新问题了!

为之奈何?

遇到这种情况是不是就完了!?

要求:

1. 主节点至少5个实例多主部署 2. 由于在需要从节点和哨兵

原理:

1. 加锁线程带着Expire时间进入,在加锁前记录一个开始加锁时间T1 2. 轮流用相同的key和value在不同的节点上进行加锁操作,并且必须保证大多数(N/2+1)节点加锁成功,才算成功 3. 最少(N/2+1)个节点加锁成功后,记录当前时间T2 4. 如果T2-T1 < Expire,则加锁成功,反之失败 5. 释放锁时,要向所有节点(不管是否在该节点加锁成功)发送解锁请求! 6. 此时,锁的Key真正有效时间为:Expire - (T2-T1) 7. 部署的节点数最好是奇数,以更好的满足过半原则

疑问:

为什么是N/2+1个节点加锁?加锁成功后,计算加锁耗时的意义?为什么释放锁时,要给所有节点(包括没有加锁成功的节点)发送解锁请求?

分析:

N/2+1公式为过半原则,这里的本质时为了容错,CAP中的P说到,当分布式系统中,如果存在部分故障节点,但大多数节点仍旧正常时,可以认为整个系统仍旧可用假如T2-T1 > Expire 就意味着一定会存在,最早加锁的节点过期自动解锁的情况,那么此时的加锁节点计数就不再正确!那么此次加锁就毫无意义了!(T2-T1为加锁时间,Expire为过期时间)假设某节点加锁成功了,但是后续因为其他原因(网络)导致无法从该节点上获取响应结果,而被判断为未成功加锁,如果只给加锁成功的节点发起解锁请求,那么此时该节点是收不到解锁请求的,就会一直持有,影响后续无法使用


理性看待

其实,Redis作者研究出来的RedLock,在一些极端的情况下是存在风险的,比如:

  • N节点的时钟存在较大偏差时,T2-T1 < Expire的讨论就是毫无意义的,依然存在琐失效的问题,想要解决这个问题,就得需要人工的去维护N节点之间的时钟趋于一致
  • RedLock仍旧解决不了获得锁的线程客户端发生长时间GC,导致锁过期,如果再出现第二个线程仍旧可以获取锁,此时,就会出现同一时刻两个线程对共享资源同时获得锁的矛盾情况,严重违反分布式锁特性中的互斥性
  • 因为RedLock无法提供类似fencing token的设计方案,从而推导出RedLock无法保证分布式的正确性

  1. 基于Zookeeper

  • 利用节点名称唯一性
  • 原理

加锁时,所有线程均在相同的目录下创建一个文件,谁先创建成功,就代表获得锁,否则就代表失败,只能等待下次当获取得锁的线程操作完业务代码后,会将该文件删除,同时通知其余客户端再次进入竞争在一个路径下只能创建一个唯一的文件(文件名唯一),但容易引起“惊群”效应


  • 利用临时顺序节点
  • 原理

所有线程刚开始都会在ZK中创建自己的临时节点,由ZK去保证这些节点的顺序加锁时,线程会判断ZK下的第一个节点是不是自己创建的,如果是,则加锁成功,如果不是,加锁失败,同时,给自己的上一个节点加一个****节点监听器当节点监听器被通知上一个节点被删除时,当前节点会重新判断ZK下第一个节点是否是自己创建的,循环2的判断操作用完锁后,每个线程只能删除自己创建的临时节点


  1. 二者对比

  • 效率:ZK锁远不如Redis锁
  • 失败处理:
  • ZK锁只需要维护Watch监听器,等待锁被释放 Redis锁则是自旋重试,高并发时耗性能
  • 宕机处理:
  • ZK是根据客户端上报心跳(长连接),判断客户端是否存在(持有锁),无心跳上报时,会删除节点(释放锁)————(客户端长GC时,锁会被ZK释放) Redis则是需要等到过期时间,才会释放锁
基于Redis实现分布式锁之前,这些坑你一定得知道 作者简介 陈寒立,一个不误正业的程序员。先后在物流金融组、物流末端业务组和压力平衡组打过杂,技术栈从Python玩到了Java,依然没学会好好写业务代码,梦想着用抽象的模型拯救业务于水火之中。 开头 基于Redis的分布式锁对大家来说并不陌生,可是你的分布式锁有失败的时候吗?在失败的时候可曾怀疑过你在用的分布式锁真的靠谱吗?以下是结合自己的踩坑经验总结的一些经验之谈。 你真的需要分布式锁吗? ... 阅读详情

相关推荐

Redis实现分布式锁

1. 为什么需要分布式锁?我们都知道,以上情况,,此时就2. 单机锁 & 分布式锁的定义:3. 分布式锁应该具备哪些条件?4. 分布式锁都有哪些主流的实现方案?分布式锁的核心是实现多线程之间互斥,而满足这一点的方式有很多,常见的有三种:补充:Zookeeper实现分布式锁的思路实现分布式锁的核心思路5. 如何用 Redis 实现分布式锁的?Redis本身可以被多个客户端访问,正好就是一个共享存储系统,可以用来保存分布式锁,而且Redis的读写性能高,可以应对高并发的锁操作场景。

weixin_53622554的博客 3282

Redisson 分布式锁出现死锁,两种解决方式

多节点应用获取单号,使用Redisson(3.10.1)分布式锁保证单号不会重复。

weixin_43365083的博客 3634

Springboot整合Redisson实现分布式锁

分布式锁简述 在传统的,老旧单机项目中要想实现锁机制,我们可以使用下面的方式 基于 Java API 层面的 Lock :代表实现类 ReentrantLock 基于 Java API 层面的 ReadWriteLock :代表实现类 ReentrantReadWriteLock 基于 JVM 的 synchronized 关键字 但随着业务场景越来越复杂,伴随着技术的不断发展,在分布式的环境中,想要实现锁机制,使用上述的方式已无法实现业务功能。所以提出了分布式锁的概念及技术 分布式锁的实现,常见的有使

桐花思雨 5129

聊聊分布式锁

动手点关注干货不迷路????为什么我们需要一把分布式锁?为了效率(efficiency),协调各个客户端避免做重复的工作。即使锁偶尔失效了,只是可能把某些操作多做一遍而已,不会产生其它的不良后果。比如重复发送了一封同样的 email(当然这取决于业务应用的容忍度)。为了正确性(correctness)。在任何情况下都不允许锁失效的情况发生,因为一旦发生,就可能意味着数据不一致...

字节跳动技术团队官方博客 4614

Zookeeper实现分布式锁详细步骤,你一定要知道

前几天分享了分布式锁的三种实现方案(我们是这样一步一步实现分布式锁的),其中对于zookeeper实现方式,有些朋友说想知道实现的总体流程。那么今天我就来将zookeeper的具体实现流程通过一步一图的形式再来说一下,然后大家可以根据前面的代码进行一起理解下,这样应该会更容易搞懂zookeeper的分布式锁。 zookeeper节点特征 现在我们都知道zookeeper是基于树形数据结构...

csdn681的博客 1100

Python学习(一)

快速入门 1.Linux系统中是可以直接使用Python进行编程,具体如下: 进入Python:在linux的Terminal中输入 Python ,进入Python的命令行; 退出Python:在linux的Terminal中输入quit() 或 exit() 或 ctrl +d ,再回车,退出Python的命令行。 2.python的源程序就是一个特殊格式的文本文件,可以使用任意文本编辑软件做Python的开发。 3.Python程序的文件扩展名通常都是 .py。 4.执行Python程序的方式:在

SummerXRT的博客 1278

【QT小作】实现看门狗功能,检测外部程序是否在运行

看门狗功能 检测外部程序是否在运行,若未运行,则启动外部程序 在子线程中实现,不卡主界面 可检测多个外部程序 .h文件 #ifndef WATCHDOGTHREAD_H #define WATCHDOGTHREAD_H #include <QObject> #include <QThread> #include <QMap> class WatchDogThread : public QThread { Q_OBJECT public: Watch

林柒Sevenlin随记 2791

机器人导航学习历程(四)local planner

local planner   我们先来回顾一下Navigation的框架,可以看出位于红框中的local_planner是move_base节点的一部分,输入包括来自global_planner的全局的规划路径、里程计信息以及local_costmap的信息,输出是速度控制指令,作为控制器的输入。 base_local_planner   局部规划一般是用base_local_planner包...

yuniruchujian的博客 4410

分布式锁那些事

在并发编程中,当多个线程需要访问共享可变资源时,需要使用锁来保证线程安全。扩展到分布式系统中,当多个节点可能会同时访问相同的资源时,为了避免数据不一致和竞争条件的发生,也需要使用分布式锁来进行协调。本篇主要对分布式锁的设计原则进行探讨,并对几种常用的解决方案做简要分析。

艾希射日 832

基于车牌识别的停车收费系统的设计与实现(源码+开题)

本系统(程序源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景: 随着城市化进程的加速和汽车保有量的增加,停车难成为了一个普遍存在的问题。传统的停车收费方式存在着效率低下、管理混乱等问题。因此,开发一款基于车牌识别的停车收费系统具有重要的现实意义。研究意义: 基于车牌识别的停车收费系统可以有效地解决传统停车收费方式在效率和准确性方面的问题,提高停车服务的效率和质量。

迟限程序设计 1391

【Stata双重差分模型】双重差分DID的具体操作步骤

本案例将详细阐述如何使用双重差分法进行实证分析,并展示具体的STATA操作代码。

a519573917的博客 2万+

关于分布式锁的面试题都在这里了|Reids分布式锁|ZooKeeper分布式锁

我今天班儿都没上,就为了赶紧把这篇文章分布式锁早点写完。我真的不能再贴心了。 边喝茶边构思,你们可不要白嫖了! 三连来一遍? 引言 为什么要学习分布式锁? 最简单的理由就是作为一个社招程序员,面试的时候一定被面啦,你看怎么多公众号都翻来覆去的发分布式锁的主题,可见它很重要啦,在高考里这就是送分题,不要怪可惜的。 那应届生也会问吗?这就不一定了,但是,如果你会,面试官肯定会多给你那点分(钱) 第三...

6012

OpenFOAM-v2006重叠网格挖洞问题实战:如何手动修复overset代码中的边缘识别缺陷

本文深入探讨了OpenFOAM-v2006中重叠网格挖洞问题的实战解决方案,重点修复overset代码中的边缘识别缺陷。通过逆向工程解析挖洞决策机制,设计最小侵入式的函数override方案,并建立多级验证体系,有效解决了网格挖洞区域覆盖不全的问题,提升了计算流体动力学模拟的准确性和可靠性。

weixin_29216049的博客 429

Java开发 - 不知道算不算详细的分布式锁详解

看,分布式锁其实也没有那么难嘛,如果你能看到这里,那么相信你一定会有所收获,多看几遍,对理解分布式锁有好处,学完分布式锁,又可以开心的出去装13了,真开心!!!学习是一个循序渐进的过程,学完之后不代表就会了,过一段时间你多半是会忘记的,就好比博主,写完之后过一段很多东西都会忘记,也需要不断的回顾才能加深印象,学习是投资自己,只有金刚钻硬了,才能去揽那瓷器活儿。加油吧,少年们!

CodingFire的博客 924

STM32裸机开发基础:寄存器直接操作详解

寄存器是微控制器内部的特殊存储单元,直接与硬件电路连接,用于控制和监视硬件功能。与普通内存不同,寄存器的每一位(bit)通常都有特定的硬件含义。寄存器就是机器的控制面板上的各种开关和指示灯每个开关控制一个具体功能(如打开LED、启动通信)每个指示灯显示一个具体状态(如数据接收完成、错误发生)// GPIO引脚定义1。

2401_88760782的博客 2186

深入解析GEM5 McPAT NoC功耗模型:从arbiter电容计算到微架构关联

本文深入解析GEM5 McPAT NoC功耗模型,重点探讨arbiter电容计算与微架构的关联。通过拆解动态功耗公式和四类电容的物理本质,揭示能量与功率的转换关系,并提供参数调试和模型精度优化的实战技巧,帮助开发者更准确地预测和优化NoC功耗。

weixin_29305313的博客 201

如何用Redis实现分布式锁

为什么需要分布式锁 在聊分布式锁之前,有必要先解释一下,为什么需要分布式锁。 与分布式锁相对就的是单机锁,我们在写多线程程序时,避免同时操作一个共享变量产生数据问题,通常会使用一把锁来互斥以保证共享变量的正确性,其使用范围是在同一个进程中。如果换做是多个进程,需要同时操作一个共享资源,如何互斥呢?现在的业务应用通常是微服务架构,这也意味着一个应用会部署多个进程,多个进程如果需要修改MySQL中的同一行记录,为了避免操作乱序导致脏数据,此时就需要引入分布式锁了。 想要实现分布式锁,必须借助一个外部系统,所有

Hopefully Sky的博客 4万+

Open3D——直通滤波(python详细过程版)【2025最新版】

python就一个字:强。博客长期更新,本文最近更新时间为:2025年1月11日。

点云侠的博客 7513

Redis的分布式锁分析

开发同学都知道,在分布式系统中,为了保证多个机器并发操作的数据一致性,常常需要使用分布式锁机制。常用的比如数据库锁,ZK锁,Reis锁等等,今天我们就探讨下Redis分布式锁,说说他的实现原理和应用场景,并介绍如何正确使用分布式锁来解决并发问题

战斧的博客 7259

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

关于分布式锁你都知道些什么?

零基础自学java,点击了解:https://how2j.cn本文章转载自(如有侵权,联系删除):https://my.oschina.net/u/4006148/blog/3165406一、为什么要使用分布式锁?我们在开发应用的时候,如果需要对某一个共享变量进行多线程同步访问的时候,可以使用我们学到的Java多线程的18般武艺进行处理,并且可以完美的运行,毫无Bug!注意这是...

java_zdc的博客 316

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

什么时候需要分布式锁?

比如: 高并发下争夺共享资源,比如秒杀对于库存这种共享资源需要用到分布式锁,如果不用分布式锁很可能造成超卖。 分布式锁也是锁 在单体应用的时候,如果多个线程要访问共享资源的时候,我们通常线程间加锁的机制,在某一个时刻,只有一个线程可以对这个资源进行操作,其他线程需要等待锁的释放,Java中也有一些处理锁的机制,比如synchronized。 而到了分布式的环境中,当某个资源可以...

La victoria的博客 1万+

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

SpringBoot实战:分布式锁你知道多少,SpringBoot + Redis实现高性能分布式锁

分布式锁是一种在分布式环境下实现互斥访问的技术,它已经广泛应用于分布式系统中。未来,分布式锁的发展趋势将继续向简单、高效、可靠的方向发展。挑战之一是在分布式环境下实现高可用的分布式锁,以确保分布式锁的可靠性。挑战之二是在分布式环境下实现低延迟的分布式锁,以提高分布式系统的性能。

Z99263的博客 2617

Redis分布式锁:你必须知道的那些事儿!

在分布式系统中,Redis分布式锁是一种基于Redis实现的锁机制,用于控制多个节点对共享资源的访问,避免并发冲突。它通过Redis的原子操作和过期时间特性,确保锁的有效性和自动释放,适用于高并发场景下的数据一致性保障。这篇文章将详细讲解Redis分布式锁的工作原理、实现方式及其在实际项目中的应用案例,帮助开发者更好地理解和使用这一核心功能。

蓝鲸鱼计算机软件开发工作室 840

分布式锁的背景,基于数据库、redis、zookeeper实现分布式锁的原理与优缺点你都知道吗?...

分布式锁的背景,基于数据库、redis、zookeeper实现分布式锁的原理与优缺点你都知道吗?   为什么要分布式锁、分布式锁的实现方式有哪几种、这几种分布式锁实现方式的优缺点有哪些?阅读完本文后你你应该掌握: 基于数据库实现分布式锁具体步骤是什么,优缺点是什么; 基于Redis实现分布式锁具体步骤是什么,优缺点是什么; 基于Zookeeper实现分布式锁具体步骤是什么,优缺点是什么; 分...

weixin_33696822的博客 3175

你知道Redis怎么才能实现分布式锁吗?

前言 分布式锁一般有三种实现方式:1. 数据库乐观锁;2. 基于Redis的分布式锁;3. 基于ZooKeeper的分布式锁。本篇博客将介绍第二种方式,基于Redis实现分布式锁。虽然网上已经有各种介绍Redis分布式锁实现的博客,然而他们的实现却有着各种各样的问题,为了避免误人子弟,本篇博客将详细介绍如何正确地实现Redis分布式锁。 可靠性 首先,为了确保分布式锁可用,我们至少要确保锁的...

awumbuk的博客 284
上一篇: 看完不信你还不懂MVCC原理详解
下一篇: 设计模式之:工厂方法模式FactoryMethodPattern的实现
Java_ttcd
博客等级 码龄4年 198粉丝 190原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值