Redis分布式锁的详细介绍

一、什么的分布式锁呢

分布式锁,即分布式系统中的锁。在单体应用中我们通过锁解决的是控制共享资源访问的问题,而分布式锁,就是解决了分布式系统中控制共享资源访问的问题。与单体应用不同的是,分布式系统中竞争共享资源的最小粒度从线程升级成了进程。

分布式锁应该具备哪些条件:

在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行
高可用的获取锁与释放锁
高性能的获取锁与释放锁
具备可重入特性(可理解为重新进入,由多于一个任务并发使用,而不必担心数据错误)
具备锁失效机制,即自动解锁,防止死锁
具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败

分布式锁的实现方式:

基于数据库实现分布式锁
基于Zookeeper实现分布式锁
基于reids实现分布式锁

本文主要讲解redis分布式锁

二、基于redis的分布式锁

redis命令说明:

(1)setnx命令:set if not exists,当且仅当 key 不存在时,将 key 的值设为 value。若给定的 key 已经存在,则 SETNX 不做任何动作。

返回1,说明该进程获得锁,将 key 的值设为 value
返回0,说明其他进程已经获得了锁,进程不能进入临界区。
命令格式:setnx lock.key lock.value

(2)get命令:获取key的值,如果存在,则返回;如果不存在,则返回nil

命令格式:get lock.key

(3)getset命令:该方法是原子的,对key设置newValue这个值,并且返回key原来的旧值。

命令格式:getset lock.key newValue

(4)del命令:删除redis中指定的key

命令格式:del lock.key

方案一:基于set命令的分布式锁

1、加锁:使用setnx进行加锁,当该指令返回1时,说明成功获得锁

2、解锁:当得到锁的线程执行完任务之后,使用del命令释放锁,以便其他线程可以继续执行setnx命令来获得锁

(1)存在的问题:假设线程获取了锁之后,在执行任务的过程中挂掉,来不及显示地执行del命令释放锁,那么竞争该锁的线程都会执行不了,产生死锁的情况。

(2)解决方案:设置锁超时时间

3、设置锁超时时间:setnx 的 key 必须设置一个超时时间,以保证即使没有被显式释放,这把锁也要在一定时间后自动释放。可以使用expire命令设置锁超时时间

(1)存在问题:

setnx 和 expire 不是原子性的操作,假设某个线程执行setnx 命令,成功获得了锁,但是还没来得及执行expire 命令,服务器就挂掉了,这样一来,这把锁就没有设置过期时间了,变成了死锁,别的线程再也没有办法获得锁了。

(2)解决方案:redis的set命令支持在获取锁的同时设置key的过期时间

4、使用set命令加锁并设置锁过期时间:

命令格式:set <lock.key> <lock.value> nx ex <expireTime>

详情参考redis使用文档:http://doc.redisfans.com/string/set.html

(1)存在问题:

① 假如线程A成功得到了锁,并且设置的超时时间是 30 秒。如果某些原因导致线程 A 执行的很慢,过了 30 秒都没执行完,这时候锁过期自动释放,线程 B 得到了锁。

② 随后,线程A执行完任务,接着执行del指令来释放锁。但这时候线程 B 还没执行完,线程A实际上删除的是线程B加的锁。

(2)解决方案:

可以在 del 释放锁之前做一个判断,验证当前的锁是不是自己加的锁。在加锁的时候把当前的线程 ID 当做value,并在删除之前验证 key 对应的 value 是不是自己线程的 ID。但是,这样做其实隐含了一个新的问题,get操作、判断和释放锁是两个独立操作,不是原子性。对于非原子性的问题,我们可以使用Lua脚本来确保操作的原子性

5、锁续期:(这种机制类似于redisson的看门狗机制)

虽然步骤4避免了线程A误删掉key的情况,但是同一时间有 A,B 两个线程在访问代码块,仍然是不完美的。怎么办呢?我们可以让获得锁的线程开启一个守护线程,用来给快要过期的锁“续期”。

① 假设线程A执行了29 秒后还没执行完,这时候守护线程会执行 expire 指令,为这把锁续期 20 秒。守护线程从第 29 秒开始执行,每 20 秒执行一次。

② 情况一:当线程A执行完任务,会显式关掉守护线程。

③ 情况二:如果服务器忽然断电,由于线程 A 和守护线程在同一个进程,守护线程也会停下。这把锁到了超时的时候,没人给它续命,也就自动释放了。

方案二:基于RedLock的分布式锁

RedLock算法是一种由Redis的作者Salvatore Sanfilippo(也称为Antirez)设计的分布式锁算法,旨在解决在分布式系统中实现可靠锁的问题。该算法通过在多个独立的Redis实例上同时获取锁来提高锁服务的可用性和安全性。以下是对RedLock算法的详细说明:

1. 算法原理

RedLock算法的核心思想是在多个Redis实例上同时尝试获取锁,只有当大多数(即超过半数)Redis实例加锁成功时,才认为成功获取了分布式锁。这样做的好处是,即使部分Redis实例发生故障,只要大多数实例仍然可用,锁服务就能继续提供服务,从而避免了单点故障的问题。

2. 算法步骤

RedLock算法的实现通常包括以下几个步骤:

  1. 获取锁
    • 客户端选择一个唯一的锁标识符(例如,UUID)。
    • 客户端尝试在多个Redis实例上同时设置锁。每个锁请求都使用相同的键和值(锁标识符),但每个Redis实例上的锁都有一个较短的过期时间(例如,几十毫秒)。
    • 客户端使用类似SET命令的原子操作来设置锁,该操作通常包含NX(Not Exists,键不存在时设置)和PX(设置键的过期时间,单位为毫秒)选项。
  2. 检查锁
    • 客户端计算成功设置锁的Redis实例的数量。
    • 如果成功设置锁的实例数量大于或等于Redis实例总数的一半加一(N/2 + 1),则客户端认为成功获取了分布式锁。
    • 如果成功设置锁的实例数量不足,则客户端会释放所有已经成功设置的锁,并重新尝试获取锁(可选)。
  3. 执行操作
    • 一旦客户端成功获取了分布式锁,它就可以安全地执行需要同步的操作。
  4. 释放锁
    • 当客户端完成操作后,它必须释放锁。释放锁时,客户端会向所有Redis实例发送释放锁的命令(例如,使用DEL命令)。
    • 如果客户端在持有锁的过程中发生故障而无法释放锁,由于设置了锁的过期时间,锁最终会自动过期释放,从而避免了死锁的发生。
3. 注意事项
  • Redis实例的选择:为了确保RedLock算法的可靠性,建议使用奇数个Redis实例(如5个),并确保这些实例分布在不同的物理或逻辑节点上,以提高系统的容错性。
  • 锁的过期时间:锁的过期时间应该设置得足够短,以避免客户端在持有锁的过程中发生故障而导致锁长时间无法释放。然而,过期时间也不能太短,以免在客户端正常执行操作时被意外释放。
  • 时间同步:RedLock算法依赖于系统的时间钟来协调操作。因此,所有Redis实例和客户端的时间钟应该保持同步,以避免因时间偏差导致的锁一致性问题。
  • 性能考虑:由于RedLock算法需要在多个Redis实例上进行交互,因此可能会引入一定的网络延迟和性能开销。在实际应用中,需要根据系统的负载和响应时间要求来评估和调整Redis实例的数量和锁的过期时间。
4. 替代方案

尽管RedLock算法在实践中被广泛使用,但它并不是没有缺陷的。由于RedLock算法需要在多个节点间进行交互,因此可能会受到网络延迟和节点超时的影响。此外,RedLock算法也依赖于Redis实例的可用性和时间同步。因此,在某些情况下,可能需要考虑使用其他替代方案来实现分布式锁,如基于ZooKeeper的分布式锁、基于etcd的分布式锁或基于数据库的分布式锁等。这些替代方案各有优缺点,需要根据具体的应用场景和需求来选择合适的实现方式。

Redis分布式锁是一种基于Redis实现的分布式锁技术。它的实现原理是利用Redis的单线程特性,通过SETNX(SET if Not eXists)指令实现锁的互斥性和EXPIRE指令设置锁的过期时间实现锁的自动释放。 以下是Redis分布式锁详细实现步骤: 1. 客户端A请求获取锁,向Redis中写入一个key,如果写入成功则视为获取锁成功,否则视为获取锁失败。 2. 如果获取锁成功,客户端A需要在一定时间内完成业务操作并释放锁,否则其他客户端无法获取锁。 3. 客户端B请求获取锁,向Redis中写入同样的key,如果写入成功则视为获取锁成功,否则视为获取锁失败。 4. 如果客户端A在规定时间内未能完成业务操作并释放锁,则Redis中的锁会过期自动释放,其他客户端可以获取锁。 5. 如果客户端B获取锁成功,则客户端A的锁失效,客户端A需要重新获取锁。 需要注意的是,Redis分布式锁的实现需要考虑以下问题: 1. 锁的持续时间不能太长,否则会导致其他客户端长时间等待。 2. 锁的持续时间不能太短,否则会导致客户端A在规定时间内未能完成业务操作并释放锁,锁被自动释放的同时,其他客户端也可以获取锁,导致锁的互斥性失效。 3. 锁的key在Redis中需要具有唯一性,否则可能会导致不同的业务操作获取到同一个锁,导致数据错乱。 4. Redis分布式锁的实现需要考虑高并发的情况,需要使用分布式锁框架来确保锁的可靠性和稳定性。 总之,Redis分布式锁是一种轻量级的分布式锁技术,具有简单、高效、可靠的特点,在实际应用中得到广泛的应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值