Redis分布式锁详细介绍

 更新时间:2021年12月06日 11:32:46   作者:-阿布-  
大家好,本篇文章主要讲的是Redis分布式锁详细介绍,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览

分布式锁

在单进程应用中,当一段代码同一时间内只能由一个线程执行时,

多线程下可能会出错,例如两个线程同时对一个数字做累加,两个线程同时拿到了该数字,例如40,一个线程加了10,一个线程加了20,正确结果应该是70,

但由于两个线程在自己的内存中一个算出的是50,一个算出的是60,此时二者都将自己的结果往该数字原本的地方写(保存),

这时候,肯定会有一个线程的值会被覆盖,因为读取->计算->保存 并不是原子操作(原子操作是指不会被线程调度机制打断的操作,这种操作一旦开始,就会一直运行结束,中间不会有任何线程切换),

也就是说最终的结果要么是50,要么是60,而不可能是70(出现并发或并行的情况下,这种情况大概率会发生),

image.png

单进程应用发生这种情况时,可以由程序提供的锁语义直接上锁(例如java中的sychornized)保证该段代码只会被一个线程执行,按照顺序来进行,结果将是正确的。

在分布式应用中,由于一台机器上可能跑着相同的应用进程,或者在不同的机器上跑着,原本程序自带的语义锁已经无法起到作用,

因为相同的代码可能是在不同的机器、进程中执行,所以此时需要一个能够让不同机器、进程中,相同的应用代码执行到同一段代码时,也能够按照顺序执行(或者同一时间内只有一个线程能够执行),

这就需要用到中间件来协调,可以实现分布式锁的中间件有很多,redis就是其中一个。

redis实现分布式锁的原理

redis中分布式锁的原理其实就是在redis当中设置一个值(当然要保证分布式应用连的都是同一个redis,以这个redis作为中间点,否则当然是没用的),这个值只能由一个线程来存放,当其他线程(或者不同机器上的进程)也来存放时,发现这个值已经存在了,就说明此时已经有人在用这把锁了,这时候要么进行重试等待,要么进行放弃。

设置一般使用 SETNX (set if not exists) 指令,如果该值没有,则进行设置,有了则不设置,这就是拿锁的关键了,当拿到锁的人执行处理完毕后,再调用 DEL 执行进行锁的释放。

死锁问题

使用 SETNX 和 DEL 实现了分布式锁,但是有一种情况,如果一个线程进行了 SETNX 拿到锁成功后,突然这个线程因为某种原因崩溃了,导致没有进行 DEL 释放锁,

那么此时,将会导致其他所有的应用都再也没办法拿到这把锁,也就是 死锁 ,这个问题的解决方式是将锁设置一个有效期,到了有效期之后该锁将被自动释放,

使用 EXPIRE 可以给锁设置一个有效期,如下

SETNX LOCK-KEY-NAME true
EXPIRE LOCK-KEY-NAME 5

但是还有一个问题,因为 SETNX 和 EXPIRE 是分为两个指令执行的,这中间依然有可能出现 SETNX 执行完毕后,由于认为或者机器、程序发生的故障 导致 EXPIRE 没有执行成功,此时还是有可能会发生死锁,

image.png

事务能不能解决这个问题?

NO,因为 EXPIRE 是依赖 SETNX 的执行结果执行的,只有 SETNX 成功后,才能进行 EXPIRE,否则是不可以执行的,事务里并没有 if else 的分支逻辑,要么全部执行,要么一个都不执行。

在 redis2.8 的版本中,引入了set指令的拓展参数,可以让 SETNX 和 EXPIRE 同时执行(原子),解决了超时问题,

SET LOCK-KEY-NAME true ex 5 nx

超时问题

上面虽然说到利用给锁设置过期时间解决可能会发生的死锁问题,但是万一我的程序代码执行时间超过了设置的过期时间,这时候锁自动释放了,但是我的代码还没执行完毕,其他人又进行执行了,导致结果出错怎么办?

在一般的开发场景中,我们会尽量将锁的时间设置的长一些,例如60s,一般应用程序在60s内都能执行完毕,但是怕就怕的是较真,如果60s内也执行不完怎么办?

此时可以使用一种续期的方案,就是当程序在执行过程中,不断的判断锁是否快要过期,代码是否执行完毕,如果快过期了没有执行完毕,就将这把锁进行续期,保证锁不会被自动释放,直到我们的代码执行完毕为止,这种方案在java中由一个叫做 redisson 的框架实现了,可以直接引入使用。

锁误放问题

在锁的使用过程中,很有可能出现其他应用没有拿到锁,但是也执行了 DEL 指令,将我们正在执行中的程序的锁释放了,导致其他地方拿到锁,进入代码段开始执行,

这里的解决方式是,在SETNX的时候,可以将value设置成一个随机生成并全局唯一的一串数字或字符,该线程一直持有字符,在释放锁的时候,将字符与锁中的字符进行比对,如果匹配,则可以进行释放锁,如果不匹配,说明是其他人误放,此时拒绝释放,

但是判断字符是否相同与释放锁并不是原子操作,redis也并不提供这么一种命令,所以我们考虑使用lua脚本执行这几步操作(redisson也实现了),

最重要的一点是,程序中使用释放锁的入口一定要统一,万一有的应用程序不使用上面所述的方法释放,直接使用 DEL ,那么上面说的方案就没用了(笔者为了测试,直接用DEL释放过)。

可重入性

可重入性是指线程在持有锁的情况下,再次请求加锁,如果一个锁支持同一个线程的多次加锁,那么这个锁就是可重入的,Java中的 ReentrantLock 就是可重入锁,大致的原理就是每次获取到锁后对一个数字进行 +1,每次释放的时候进行 -1,当数字为0时,分布式锁被释放,

redis锁如果要支持可重入性,也需要用上面的方式进行支持,不过该逻辑加重了复杂性,一般推荐将需要锁的代码段进行逻辑调整,避免重复获取分布式锁来处理。(当然redisson也支持了可重入锁)

Redlock

上面的方式看起来没有太多的问题了,但是由于redis本身可能也会发生问题,例如在Sentinel集群中,主节点挂掉,从节点变成主节点,但是客户端这时候是不知道的,

如果客户端在刚刚挂掉的主节点上SETNX成功了,但是这把锁还没有同步到从节点中,从节点这时候变成了主节点,这时候新主节点中没有这把锁的信息,

此时另一个客户端来请求这把锁,直接 SETNX 成功,又导致了两个客户端同时在执行相同的代码,又出现了不安全性,

image.png

为此业界提供了叫做 Redlock 的解决方案,原理大致是,提供多台redis实例,这些实例之间相互独立,没有主从关系(没有任何关系),同其他分布式算法一样,使用了大多数机制,

加锁时,它会向过半节点发出 set(key, value, nx = True, ex = xxx)指令,只要过半节点设置成功,这把锁就算拿到了,释放锁时向所有节点发出 DEL 指令,

Redlock算法(Redisson已支持)需要考虑出错重试,时钟漂移等等细节问题,同时Redlock需要向多个节点进行读写,性能将要比单例redis下降,

如果业务场景对错误的发生容忍度很低,又可以接受性能稍微有点下降,可以考虑采用Redlock算法。

到此这篇关于Redis分布式锁详细介绍的文章就介绍到这了,更多相关Redis分布式锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Redis 在真实世界的 5 个用法

    Redis 在真实世界的 5 个用法

    Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API这篇文章主要介绍了Redis 在真实世界的 5 个用法,需要的朋友可以参考下
    2018-03-03
  • 一文掌握Redis的三种集群方案(小结)

    一文掌握Redis的三种集群方案(小结)

    这篇文章主要介绍了一文掌握Redis的三种集群方案(小结),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-02-02
  • 解决redis在linux上的部署的问题

    解决redis在linux上的部署的问题

    这篇文章主要介绍了redis在linux上的部署,本文分步骤给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-02-02
  • Centos7下Redis3.2.8最新版本安装教程

    Centos7下Redis3.2.8最新版本安装教程

    这篇文章主要为大家详细介绍了Centos7下Redis3.2.8最新版本的安装教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-04-04
  • Redis可视化客户端小结

    Redis可视化客户端小结

    因为 Redis 官方只提供了命令行版的 Redis 客户端 redis-cli,以至于我们在使用的时候会比较麻烦,而且命令行版的客户端看起来也不够直观,下面是我这些年使用过的一些 Redis 可视化客户端,分享给大家
    2021-06-06
  • redis5.0以上基于密码认证的集群cluster方式

    redis5.0以上基于密码认证的集群cluster方式

    这篇文章主要介绍了redis5.0以上基于密码认证的集群cluster方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • 一文带你深入理解Redis的主从架构

    一文带你深入理解Redis的主从架构

    Redis主从架构是一种分布式数据库架构,它包括一个主节点(Master)和一个或多个从节点(Slave),主节点处理所有写操作,从节点负责复制主节点的数据并处理读请求,本文将带大家深入理解Redis主从架构,需要的朋友可以参考下
    2023-09-09
  • 如何使用redis中的zset实现滑动窗口限流

    如何使用redis中的zset实现滑动窗口限流

    滑动窗口限流是一种常见的流量控制方法,它限制了在一定时间窗口内的请求数量,下面是使用Redis ZSet实现滑动窗口限流的一个简单示例,需要的朋友可以参考下
    2023-09-09
  • Redis删除过期key策略详解

    Redis删除过期key策略详解

    Redis是一款高性能的开源内存数据库,广泛应用于缓存、消息队列、实时分析等场景,在Redis中,我们经常需要删除过期的key,以释放内存空间并保持数据的有效性,本文将为您详细介绍Redis的过期key删除策略,帮助您更好地管理和优化Redis数据库
    2023-10-10
  • 详解Redis高效恢复策略内存快照与AOF

    详解Redis高效恢复策略内存快照与AOF

    这篇文章主要为大家介绍了Redis高效恢复策略内存快照与AOF及对比详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12

最新评论