大厂面试官问我:Redis的分布式锁,锁失效了该怎么办?【后端八股文十:Redis分布式锁八股文合集(1)】

 本文为【Redis分布式锁八股文合集(1)】初版,后续还会进行优化更新,欢迎大家关注交流~

hello hello~ ,这里是绝命Coding——老白~💖💖 ,欢迎大家点赞🥳🥳关注💥💥收藏🌹🌹🌹
19d95742d45b4220ad0ae0359ffcba93.png

💥个人主页绝命Coding-CSDN博客
💥 所属专栏后端技术分享
这里将会不定期更新有关后端、前端的内容,希望大家多多点赞关注收藏💖

    往期内容:

大厂面试官问我:Redis处理点赞,如果瞬时涌入大量用户点赞(千万级),应当如何进行处理?【后端八股文一:Redis点赞八股文合集】-CSDN博客

大厂面试官问我:布隆过滤器有不能扩容和删除的缺陷,有没有可以替代的数据结构呢?【后端八股文二:布隆过滤器八股文合集】-CSDN博客

大厂面试官问我:Redis持久化RDB有没有可能阻塞?阻塞点在哪里?【后端八股文三:Redis持久化八股文合集】-CSDN博客

大厂面试官问我:Redis内存淘汰,LRU维护整个队列吗?【后端八股文四:Redis内存淘汰策略八股文合集】-CSDN博客

下篇文章:

大厂面试官问我:Redis的分布式锁怎么解决死锁问题?【后端八股文十:Redis分布式锁八股文合集(2)】

 

分布式锁

Java的锁只能保证单机的时候有效,分布式集群环境就无能为力了,这个时候我们就需要用到分布式锁

 

常见的解决方案

 

基于关系型数据库,如MySQL
基于关系型数据库实现分布式锁,是依赖数据库的唯一性来实现资源锁定,比如主键和唯一索引等。

 

基于Redis实现
Redis 锁实现简单,理解逻辑简单,性能好,可以支撑高并发的获取、释放锁操作。
缺点:

 

  • Redis 容易单点故障,集群部署,并不是强一致性的,锁的不够健壮;
  • key 的过期时间设置多少不明确,只能根据实际情况调整;
  • 需要自己不断去尝试获取锁,比较消耗性能。

 

Redis实现分布式锁

 

加锁:
使用setnx来加锁。key是锁的唯一标识,按业务来决定命名,value这里设置为test。

 

setx key test

 

解锁
有加锁就得有解锁。当得到的锁的线程执行完任务,需要释放锁,以便其他线程可以进入。释放锁的最简单方式就是执行del指令。

 

del key

 

锁超时
锁超时知道的是:如果一个得到锁的线程在执行任务的过程中挂掉,来不及显式地释放锁,这块资源将会永远被锁住,别的线程北向进来。

 

所以,setnx的key必须设置一个超时时间,以保证即使没有被显式释放,这把锁也要在一段时间后自动释放。setnx不支持超时参数,所以需要额外指令,

 

expire key 30

 

存在的问题

 

SETNX 和 EXPIRE 非原子性

 

由于setnx指令本身是不支持传入超时时间的,而在Redis2.6.12版本上为set指令增加了可选参数, 用法如下:

 

SET key value [EX seconds][PX milliseconds] [NX|XX]

 

锁误解除

 

如果线程 A 成功获取到了锁,并且设置了过期时间 30 秒,但线程 A 执行时间超过了 30 秒,锁过期自动释放,此时线程 B 获取到了锁;随后 A 执行完成,线程 A 使用 DEL 命令来释放锁,但此时线程 B 加的锁还没有执行完成,线程 A 实际释放的线程 B 加的锁。

 

解决办法:

 

在del释放锁之前加一个判断,验证当前的锁是不是自己加的锁。

 

具体在加锁的时候把当前线程的id当做value,可生成一个 UUID 标识当前线程,在删除之前验证key对应的value是不是自己线程的id。

 

还可以使用 lua 脚本做验证标识和解锁操作。

 

超时解锁导致并发

 

如果线程 A 成功获取锁并设置过期时间 30 秒,但线程 A 执行时间超过了 30 秒,锁过期自动释放,此时线程 B 获取到了锁,线程 A 和线程 B 并发执行。

 

A、B 两个线程发生并发显然是不被允许的,一般有两种方式解决该问题:

 

  • 将过期时间设置足够长,确保代码逻辑在锁释放之前能够执行完成。
  • 为获取锁的线程增加守护线程,为将要过期但未释放的锁增加有效时间。

 

4、不可重入

 

当线程在持有锁的情况下再次请求加锁,如果一个锁支持一个线程多次加锁,那么这个锁就是可重入的。如果一个不可重入锁被再次加锁,由于该锁已经被持有,再次加锁会失败。Redis 可通过对锁进行重入计数,加锁时加 1,解锁时减 1,当计数归 0 时释放锁。

 

 

Redis 为什么能做分布式锁

  1. 原子性操作: Redis 提供了一些原子性操作,如 SET key value NX 命令,可以确保在同一时刻只有一个客户端成功获取锁。这种原子性操作可以确保分布式环境下的竞争条件。
  2. 持久性: Redis 是基于内存的数据库,数据可以持久化到磁盘,即使系统重启,之前获取的锁也不会丢失。这保证了分布式锁的持久性。
  3. 可靠性: Redis 是一个高可用的分布式系统,通过复制和故障转移机制,可以确保即使部分节点宕机,锁也不会丢失。
  4. 性能: Redis 的读写性能非常高,可以满足分布式环境下对锁的性能需求。

 

Redis分布式锁会有什么问题?是cp还是ap?有没有其它解决方案?/ Redis分布式锁缺陷? / Redis分布式锁有什么问题 为什么需要用lua脚本?

  1. 单点故障: 如果 Redis 服务器宕机,将会导致整个分布式系统无法获取锁,存在单点故障风险。
  2. 锁超时问题: 客户端在获取锁后可能由于各种原因(网络问题、程序异常等)而无法及时释放锁,这会导致其他客户端无法获取锁,出现死锁问题。
  3. 安全性问题: 如果客户端在获取锁后崩溃,而没有正确释放锁,其他客户端将无法获取锁。为了解决这个问题,通常需要使用 Lua 脚本来实现锁的释放,以确保原子性。

 

Redis 分布式锁属于 CP 模型,即满足一致性(Consistency)和分区容忍性(Partition Tolerance),但不能完全满足可用性(Availability)。

原因如下:

  1. 一致性(Consistency): Redis 分布式锁通过原子性操作(如 SET key value NX)来保证同一时刻只有一个客户端获取到锁,满足一致性要求。
  2. 分区容忍性(Partition Tolerance): Redis 作为一个分布式系统,能够容忍网络分区的发生,仍然能够提供服务。
  3. 可用性(Availability): 由于 Redis 分布式锁存在单点故障的风险,当 Redis 服务器宕机或网络分区导致无法连接 Redis 时,客户端就无法获取锁,违背了可用性原则。

因此,Redis 分布式锁属于 CP 模型,即满足一致性和分区容忍性,但不能完全满足可用性。为了提高可用性,可以考虑使用 Redlock 算法或 Zookeeper 等其他分布式锁方案

 

为了解决这些问题,可以考虑以下方案:

  1. Redlock 算法: Redlock 是一种基于多个 Redis 实例的分布式锁算法,可以提高可靠性和可用性。
  2. Zookeeper 分布式锁: Zookeeper 提供了一种基于 Znode 的分布式锁机制,可以解决 Redis 单点故障的问题。
  3. Consul 分布式锁: Consul 也提供了一种基于 Key-Value 存储的分布式锁机制,可以作为 Redis 分布式锁的替代方案。
  4. 基于数据库的分布式锁: 可以使用数据库的行级锁或表级锁来实现分布式锁,这种方式可靠性较高,但性能可能不如 Redis 或 Zookeeper。

 

为什么需要使用Lua脚本:

    • Lua 脚本可以确保释放锁的操作是原子性的,避免了竞争条件。
    • 使用 Lua 脚本可以检查当前锁的所有者是否与当前客户端一致,从而避免误删除其他客户端的锁。

 

为什么能满足分布式环境下的需求?

  • 原子性: Redis 的 SET key value NX 命令是原子性的,可以确保同一时刻只有一个客户端获取到锁。
  • 持久性: Redis 支持数据持久化,即使系统重启,之前获取的锁也不会丢失。
  • 高可用性: Redis 集群支持主从复制和故障转移,能够提高可用性,减少单点故障。

 

Redis的分布式锁,锁失效了该怎么办?

  • 如果锁超时或客户端崩溃导致锁失效,可以设置一个自动续期机制,定期检查并延长锁的过期时间。
  • 如果锁已经失效,可以让客户端重新尝试获取锁。

 

Redis的分布式锁是公平锁还是非公平锁?

  • Redis 分布式锁是非公平锁,因为获取锁的顺序取决于客户端的执行速度,而不是按照请求顺序。

 

Redis分布式锁主从架构锁失效问题如何解决?

  • 可以使用 Redlock 算法,它在多个 Redis 实例上获取锁,只有在大多数实例上获取成功才算获取锁成功。这样可以提高可用性和容错性。

 

Redlock 算法是什么?

Redlock 算法是一种用于实现分布式锁的算法,它建立在 Redis 分布式锁的基础之上,旨在提高分布式锁的可用性和容错性。

Redlock 的主要实现步骤如下:

  1. 获取当前时间戳
  2. 尝试在 N 个独立的 Redis 实例上获取锁
    • 在每个 Redis 实例上执行 SET key value NX PX milliseconds 命令获取锁
    • 如果在大多数 Redis 实例(N/2 + 1)上成功获取锁,则认为获取锁成功
  1. 计算获取锁总共耗时
  2. 如果获取锁成功,且总耗时小于锁的有效时间的 1/2,则认为获取锁成功
  3. 如果获取锁失败,则在所有成功获取锁的实例上执行 DEL key 命令来释放锁

Redlock 算法的优点如下:

  1. 提高可用性: 即使某个 Redis 实例不可用,只要大多数实例可用,客户端仍能获取到锁,提高了可用性。
  2. 提高容错性: 即使某个 Redis 实例出现问题,客户端也能在其他实例上获取到锁,减少了单点故障的风险。
  3. 强一致性: Redlock 算法采用"大多数获胜"的方式,可以确保同一时刻只有一个客户端获取到锁,满足强一致性要求。

缺点是:

  1. 需要多个独立的 Redis 实例,增加了系统复杂度和部署成本。
  2. 如果大部分 Redis 实例不可用,客户端可能无法获取到锁,降低了可用性。

 

用原生redis实现分布式锁,锁误删的情况?

  • 使用 Lua 脚本可以确保释放锁的操作是原子性的,避免了竞争条件和误删除其他客户端的锁。

 

Redis分布式锁底层实现?

  • Redis分布式锁的底层主要依赖于Redis的SET key value NX命令。
  • 该命令可以确保在同一时刻只有一个客户端成功获取锁。如果key已经存在,则返回NULL,否则将key-value对设置成功并返回OK。
  • 释放锁则是通过DEL key命令来删除对应的key。

 

单个节点和多个节点的redis分布式锁有什么区别?

  • 单个节点的Redis分布式锁:
    • 只需要在单个Redis实例上获取和释放锁。
    • 如果Redis实例宕机或网络中断,则会导致锁失效,出现单点故障问题。
  • 多个节点的Redis分布式锁(Redlock算法):
    • 需要在多个独立的Redis实例上获取锁。
    • 只有在大多数Redis实例(N/2 + 1)上成功获取锁,才算获取锁成功。
    • 即使有个别Redis实例不可用,只要大多数实例可用,客户端仍能获取到锁,提高了可用性和容错性。

 

除了redis还有哪些可以实现分布式锁? / 可以做分布式锁的除了redis还有哪些?/ 除了Redis,ZooKeeper还知道哪些分布式锁

  • Zookeeper: Zookeeper提供了持久化的顺序节点,可以实现分布式锁。Zookeeper分布式锁具有高可用性和强一致性。
  • Mysql:优点是实现简单,容易理解。缺点是性能一般,并且需要额外维护一张锁表。

 

redis分布式锁,为什么value设置成uuid,避免误删?细说场景

  • 设置value为UUID是为了避免误删其他客户端的锁。
  • 如果不设置UUID,当一个客户端获取锁后宕机或网络中断,无法释放锁。此时其他客户端尝试删除锁时,可能会误删除其他客户端的锁。
  • 设置value为UUID后,客户端在释放锁时可以先检查value是否等于自己的UUID,从而确保只释放自己持有的锁,避免了误删除的问题。

 

分布式锁怎么选型,zk和redis 比较下 / 为什么用redis分布式锁,而不是考虑用别的锁 / Redis 实现分布式锁 和 ZooKeeper 实现分布式锁优缺点对比?/ zookeeper结构 与redis分布式锁区别 / Redis 和 zookeeper 那个好,如果是集群部署,高并发情况下哪个性能更好

 

  1. 性能:
    • Redis 分布式锁的性能较高,Redis 本身是一个高性能的内存数据库,获取和释放锁的操作都很快。作为一个内存数据库,在单机和集群场景下都具有非常出色的性能和吞吐量。在高并发场景下,Redis 分布式锁的性能会更加优秀。
    • ZooKeeper 分布式锁相对来说性能略差一些,因为 ZooKeeper 是一个专门的分布式协调服务,涉及多次 RPC 调用。作为一个分布式协调系统,性能相对会弱一些。但通过合理的集群配置,ZooKeeper 也可以支撑较高的并发场景。
  1. 可靠性和容错性:
    • ZooKeeper 分布式锁具有更高的可靠性和容错性,因为 ZooKeeper 是一个高可用的分布式系统,即使个别节点宕机,也不会影响整体的可用性。
    • Redis 分布式锁如果使用单节点,则存在单点故障的风险。不过可以采用 Redis Cluster 或者 Redis Sentinel 等方式来提高可用性。
  1. 实现复杂度:
    • Redis 分布式锁的实现比较简单,只需要利用 Redis 的 SET key value NX 命令即可。
    • ZooKeeper 分布式锁的实现相对复杂一些,需要利用 ZooKeeper 的顺序节点和监听机制来实现。
  1. 一致性:
    • ZooKeeper 分布式锁能提供更强的一致性保证,因为 ZooKeeper 是基于 Paxos 算法实现的。
    • Redis 分布式锁虽然也可以通过 Redlock 算法来提高一致性,但实现相对复杂。
  1. 使用场景:
    • 对于追求高性能和简单实现的场景,Redis 分布式锁是一个不错的选择。
    • 对于追求高可用性和强一致性的场景,ZooKeeper 分布式锁可能更合适。

 

为什么zk实现的分布式锁可以做到强一致,redis分布式不能

  1. 一致性模型:
    • ZooKeeper 使用 Paxos 算法实现了强一致性的复制状态机。这意味着 ZooKeeper 集群中的所有节点都能保证数据的一致性。
    • Redis 虽然也有集群模式,但它采用的是最终一致性模型,即节点之间的数据可能会存在短暂的不一致。
  1. 锁机制:
    • ZooKeeper 分布式锁是基于顺序节点实现的。客户端在获取锁时会在一个特定的节点下创建一个有序的子节点,子节点的序号决定了获得锁的顺序。这种机制能够保证锁的公平性和一致性。
    • Redis 分布式锁一般是通过 SET key value NX 这种原子操作实现的,这种方式无法保证完全的公平性和一致性。
  1. 故障处理:
    • ZooKeeper 集群采用主备复制,即使个别节点宕机,集群仍能保持运行。同时 ZooKeeper 还提供了节点监听机制,可以及时感知并处理节点故障。
    • Redis 如果使用单节点,存在单点故障的风险。即使使用了 Redis Cluster,在节点故障时也可能会出现短暂的不一致。

 

是否了解Redisson分布式锁的实现原理

  • Redisson 采用了一种称为"红锁"的算法,结合了 Redis 的 SET key value NX 命令和 Redlock 算法。
  • 具体来说,Redisson 会在多个 Redis 节点上同时获取锁,只有当大多数节点(例如 3 个节点中的 2 个)都成功获取锁,才认为整个分布式锁获取成功。这种方式可以提高分布式锁的可靠性。

 

使用单个键作为分布式锁的热键问题,如何解决

  • 使用单个键作为分布式锁会导致该键成为热点,承受很大的访问压力。
  • Redisson 的解决方案是:根据锁的名称计算出多个 Redis 键,在这些键上进行加锁和释放锁操作,从而分散热点压力。

 

为什么需要使用Redisson加看门狗实现分布式锁

  • 在使用分布式锁时,客户端可能会由于各种原因(例如网络中断、程序崩溃等)无法及时释放锁,导致锁一直被占用。
  • Redisson 的看门狗机制可以解决这个问题。它会定期检查锁的状态,如果发现锁已经过期,则会自动释放该锁,防止出现死锁的情况。

 

Redission的watchdog原理

  • Redisson 的看门狗是一个独立的线程,它会定期去检查所有获得锁的客户端,判断锁是否已经过期。
  • 如果发现锁已经过期,看门狗会自动释放该锁,防止死锁的发生。

 

   后期新的八股文合集文章会继续分享,感兴趣的小伙伴可以点个关注~

 更多精彩内容以及免费资料请关注公众号:绝命Coding

914cbb12b2c3492aaa31232a11aa9c64.png

 

 

 

  • 16
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值