redis分布式锁的原子性和可靠性

要保证redis操作的原子性,一般来说有两种方式,一种是使用redis的单命令操作,另一种则是使用lua脚本保证原子性。

结合分布式锁,我们一般使用SETNX命令来完成加锁,DEL命令来完成锁的释放,虽然setnx是原子性的,但是setnx+del

并不是原子性的,所以其中便产生风险点

风险1:成功加锁之后,由于业务异常导致锁并没有释放,这导致其他客户点无法获取到锁,在redis层面,我们应当

通过增加锁的过期时间。

风险2:就是redis并区分不了客户端的操作是否是来自同一客户端,举个例子,A执行setnx,B执行del,C执行setnx,

这就会造成A和C都获取到了锁。

解决方案:redis给set命令实现了类似SETNX的功能,针对上述的问题,SET lock_key unique_value NX PX 10000,

我们通过开启set命令的NX和PX选项,NX起到不存在即设置的效果,PX则是毫秒级的过期时间,然后我们通过对value

设置特殊标识,以便于DEL操作的时候,判断是否是加锁的客户端删除,而不是别的客户端导致的误删。

在执行DEL的时候,需要读取锁变量的值,判断是否等于执行DEL操作的客户端的标识,一致才满足才执行删除,所以一般DEL操作

通过lua脚本完成以保证其原子性。然后问题来了,如果我们的redis实例宕机了,锁的可靠性得不到保证,怎么办?


随着redis锁的可靠性的问题的产生,分布式锁算法redlock应运而生,redlock的思路是客户端和多个redis实例请求加锁,

如果能和半数以上的实例加锁成功,则认为获得分布式锁。这样做可以保证在有redis实例宕机的情况下,其他实例也有锁变量,

不会影响客户端的正常进行。具体操作如下

使用 SET 命令,带上 NX,EX/PX 选项,以及值设置为客户端的唯一标识,为了防止加锁的时候实例宕机,需要设置一个

加锁超时时间,超时便跟下一个实例执行加锁操作,要注意的是加锁的超时时间,需要远小于锁的有效时间,不然等加完锁

留给业务逻辑执行的时间就不多了。

怎么算加锁成功呢?需要和超过半数的实例完成加锁,加锁的总耗时小于锁的有效时长,然后计算锁的剩余有效时间,

如果来不及完成业务共享数据的操作,那么便释放锁。

总结:使用分布式锁,需要注意操作的原子性,以及异常情况的导致锁无法释放情况,通过设置超时时间解决,

释放锁的时候,需要设置客户端唯一标识以避免出现误删情况,使用lua脚本保证DEL操作的原子性,

通过redlock,实现高可用分布式锁。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容