Redis 并发锁
Posted 飞鱼的梦呓
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis 并发锁相关的知识,希望对你有一定的参考价值。
阅读本文大约需要 5 分钟
大家好,我是飞鱼。又到周末了,今天给大家分享下通过 Redis 实现并发锁的相关知识。
使用场景
首先使用分布式锁的场景一般是并发读取公有资源,例如加减库存这类业务场景。
大体的思路就是在读取公共资源前,先去获取锁,如果能抢到锁,则进行下一步操作,否则操作失败。
分类
分布式锁可以分为单机锁与分布式锁。
单机锁指的是锁存储在单个节点上,分布式锁就是存储在多节点上。
1.单机锁
单机锁在 Redis 中可以用一个 string 类型的数据表示,假设键名为 lock ,键值为 1 或 0,其中 1 代表有客户端获取到了锁,0 代表没有客户端获取到锁。
加锁流程
客户端的获取锁的步骤为
通过 get lock 命令获取锁;
若结果为 1,代表成功获取到锁,执行后续操作2;
若结果为 0,代表获取锁失败,返回失败提示;
执行业务逻辑,完成后通过 del lock 命令释放锁
上面流程对应的伪代码如下
其中 acquire_lock 函数包括了获取锁变量,判断锁变量值,设置锁变量这三个步骤,为了保证这三个步骤的原子性,需要通过 Redis 的原子命令或者 lua 脚本完成,原子命令的优先级由于比 lua 脚本简单易实现,因此选择原子命令的优先级更高。
Redis 中的 SETNX 命令可以解决加锁进行的相关操作。SETNX 命令,它用于设置键值对的值。具体来说,就是这个命令在执行时会判断键值对是否存在,如果不存在,就设置键值对的值,如果存在,就不做任何设置。
因此,通过 SETNX 和 DEL 命令实现分布式锁的获取与释放的伪代码如下
简单分析下上面的代码,如果客户端在成功获取到锁之后,在执行业务逻辑阶段由于故障宕机了,就会导致执行不到 DEL lock 阶段,也就是说锁永远都无法释放了。这样肯定是不行的,因此,一般来说,都会给 lock 设置一个过期时间来防止这种情况的发生。
那么问题又来了,过期时间设置多久好呢?
这个过期时间需要根据实际业务来设置,至少要大于业务逻辑执行的时间,不然业务还没执行完,锁就被释放了,肯定也是不行的。
设置了过期时间还不够,再来考虑下面的一个场景
如果客户端 A 执行了 SETNX 命令加锁后,假设客户端 B 执行了 DEL 命令释放锁,此时,客户端 A 的锁就被误释放了。如果客户端 C 正好也在申请加锁,就可以成功获得锁,进而开始操作共享数据。这样一来,客户端 A 和 C 同时在对共享数据进行操作,数据就会被修改错误。
为了应对上面的场景,需要把锁变量值设置为客户端的唯一标识,只能由自己释放自己加的锁。
因此,可以使用下面命令完成加锁
解锁流程
讲完了加锁的实现,接下来看看释放锁的具体实现。
由于释放锁时会涉及到:获取锁变量,判断锁变量以及删除锁变量这三个步骤,并且这三个步骤需要保证原子性。
Redis 中并不存在能够完成这三个步骤的单命令,因此释放锁需要通过 Lua 脚本来完成。
对应的伪代码如下所示
其中,KEYS[1]表示 lock_key,ARGV[1]是当前客户端的唯一标识,当我们执行下面的命令,就可以完成锁释放操作了。
小结
基于 Redis 单节点实现的分布式锁,先通过 set ex nx 命令获取到锁,如果可以获取到锁,就执行业务逻辑,再通过 Lua 脚本来释放锁。
2.分布式锁
上面的单节点锁由于存在单点故障的原因,由此便产生了分布式锁,也叫做 RedLock。
Redlock 加锁算法的基本思路
让客户端和多个独立的 Redis 实例依次请求加锁,如果客户端能够和半数以上的实例成功地完成加锁操作,那么就认为客户端成功地获得分布式锁了,否则加锁失败。
这样一来,即使有单个 Redis 实例发生故障,因为锁变量在其它实例上也有保存,所以,客户端仍然可以正常地进行锁操作,锁变量并不会丢失。
整个加锁过程涉及下面三步操作
1.获取当前时间
2.依次向 Redis 实例请求加锁
3.计算获取到锁的耗时
其中,客户端成功获取到锁时必须同时满足以下两个条件:
1.从超过半数的实例获取锁成功
2.获取锁的总耗时没有超过锁的有效时间
如果没能满足上面的条件,就需要依次向 Redis 实例执行释放锁命令,具体实现与单节点版一致,都是通过 Lua 脚本完成。
参考
《极客时间 Redis 核心技术与实战》
总结
通过在共享存储区中设置变量,可以实现分布式锁。Redis 本身具备高可用的共享存储功能 ,因此使用 Redis 来实现分布式锁十分常见。
单节点锁主要通过 set nx ex 命令完成加锁,再通过 Lua 脚本来释放锁。由于单节点本身并不可靠,因此又诞生了 RedLock 锁。
RedLock 锁作为一种高可用分布式锁的解决方案,避免了单点故障的问题,即使集群中有某个节点下线,也可以保证锁能够正常被获取,大大提高了可用性。
以上是关于Redis 并发锁的主要内容,如果未能解决你的问题,请参考以下文章