2022-04-17 07:29:12
什么是 Redis 分布式锁?能干什么,解决了什么痛点?
Redis 分布式锁是利用 Redis 的特性实现的分布式环境下的锁机制。它主要用于解决在分布式系统中,多个节点需要访问共享资源时的同步问题,确保同一时间只有一个节点能够访问该资源,从而避免数据不一致和并发冲突。
纯 Redis 命令可以通过 SETNX 命令实现一个基本的分布式锁。SETNX 命令在键不存在的情况下设置键的值,并返回 1 表示获取锁成功,如果键已存在,则返回 0 表示获取锁失败。但这种方法不是原子操作,因为它涉及 SETNX 和 EXPIRE 两个命令,可能导致锁无法释放的问题(尤其在高并发场景下)。因此,这种方法更适合低并发场景。
Redis 除了可以用来做缓存,还可以用来做分布式锁,这是 Redis 的一个重要应用场景。
为什么需要分布式锁?
在分布式系统中,由于多个节点可能同时访问共享资源,如果没有有效的同步机制,就可能导致数据不一致、资源竞争等问题。分布式锁就是为了解决这些问题而设计的,它能够确保同一时间只有一个节点能够访问共享资源,从而保证数据的一致性和系统的稳定性。



Redis 集群中,分布式锁可能会出现的问题
在 Redis 集群中,虽然保证了高可用性(AP),但存在数据同步的问题。当主机收到消息后(还未同步到从机),如果主机宕机,集群中的从机上位,这块消息就会存在丢失问题,导致分布式锁失效。
手写分布式锁
为什么 Redis 分布式锁一定要给 key 设置过期时间?
如果微服务中的一个节点在执行释放 Redis 锁时挂掉,那么 Redis key 将永远无法过期,其他节点就永远无法获取到锁,从而导致死锁。因此,为 Redis key 引入过期时间是必要的。

然而,为 Redis key 引入过期时间也会引入其他问题,如锁被误释放等。解决方案是在设置锁时,将当前节点的唯一标识(如 UUID)作为 value,释放锁时先判断 value 是否匹配,再删除 key。

在高并发场景下,如何保证释放的锁是自己的锁?
可以通过在释放锁时,先获取当前锁的 value,并与自己的唯一标识进行比较,如果匹配则删除 key,从而确保释放的是自己的锁。


是不是只能用 Redis 做分布式锁,别的行不行,比如 MySQL、ZooKeeper?如果别的可以,为什么选择用 Redis 做分布式锁?
虽然 MySQL、ZooKeeper 等也可以实现分布式锁,但 Redis 在性能和易用性方面具有优势。Redis 是基于内存的数据库,读写速度非常快,适合作为分布式锁的存储介质。同时,Redis 提供了丰富的命令和数据结构,可以方便地实现各种复杂的锁机制。而 MySQL 是关系型数据库,读写速度相对较慢,且实现分布式锁需要额外的设计和优化。ZooKeeper 虽然也可以实现分布式锁,但其复杂性和性能开销相对较高。因此,在大多数情况下,Redis 是实现分布式锁的首选方案。

Redis 分布式锁到期后如何续期?看门狗模式
为了解决 Redis 分布式锁到期后自动释放的问题,可以采用看门狗模式。看门狗模式是指一个后台线程定期检测锁的过期时间,并在锁即将过期时自动续期。这样可以确保在业务操作未完成时,锁不会被误释放。但需要注意的是,看门狗模式可能会增加系统的复杂性和开销。
Lua 脚本什么是 Lua 脚本?
Lua 脚本是一种类似于 shell 脚本的语言,可以将一些简单的命令组合起来,形成一个复杂的功能。在 Redis 中,Lua 脚本可以用于组合多个 Redis 命令,保证其在高并发场景下的原子性。

Lua 脚本有什么用?
Lua 脚本在 Redis 中主要用于保证多个命令的原子性执行。在高并发场景下,如果多个命令分别执行,可能会因为网络延迟、系统抖动等原因导致命令的执行顺序和预期不一致。而使用 Lua 脚本将多个命令组合在一起执行,可以确保这些命令在 Redis 服务器端以原子性的方式执行,从而避免上述问题。

基础语法使用
Lua 脚本的基础语法包括变量定义、条件判断、循环语句等。在 Redis 中执行 Lua 脚本时,需要使用 EVAL 命令。EVAL 命令的第一个参数是 Lua 脚本本身,后面的参数是脚本中需要用到的 Redis 键和值。





Lua 脚本在分布式锁中的应用:将释放锁中判断 value 与删除 key 合成一个原子性操作。这样可以确保在释放锁时,先判断当前锁的 value 是否匹配,再删除 key,从而避免误释放锁的问题。



Redlock 算法解决的痛点