redis之分布式锁

本文深入探讨了Redis分布式锁的实现、避免死锁的方法、处理锁过期及释放问题,以及Redlock的安全性争议。作者分析了Redis作者与分布式系统专家Martin关于Redlock的论点,指出在特定异常情况下,Redlock可能存在的问题。此外,文章还讨论了基于Zookeeper的锁安全性和优缺点,以及如何结合分布式锁和fencing token策略来保证正确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

优快云云计算公众号
以下文章来源于水滴与银弹 ,作者Magic Kaito

核心问题:

在这里插入图片描述

为什么要使用分布式锁?

再开始讲分布式锁之前,有必要简单介绍一下,为什么需要分布式锁?

与分布式锁相对应的是【单机锁】,我们在写多线程程序的时候,避免同时操作一个共享变量产生数据问题,通常会使用一把锁来【互斥】,以保证共享变量的正确性,其使用范围是【同一进程】中。

如果换做是多个线程,需要同时操作一个共享资源,如何互斥呢?

例如,现在的业务应用通常都是微服务架构,这也意味着一个应用会部署多个进程,那这多个进程如果需要修改MySql中的同一行记录时,为了避免操作乱序倒置数据错误,此时,我们就需要引入【分布式锁】来解决问题了。

想要实现分布式锁,必须借助一个外部系统,所有进程都去这个系统申请【加锁】,而这个外部系统,必须要实现【互斥】的能力,即两个请求同时进来,只会给一个进程返回成功,另一个返回失败(或等待),这个外部系统,可以是MySql,也可以是Redis或者Zookeeper,但为了追求更好的性能,我们通常会选择Redis或Zookeeper来做。

分布式锁如何实现?

我们从最简单的开始讲起。
想要实现分布式锁,必须要求Redis有【互斥】的能力,我们可以使用SETNX命令,这个命令表示SET IF NOT EXISTS
,即如果key不存在,才会设置它的值,否则什么都不做。

两个客户端进程可以执行这个命令,达到互斥,就可以实现一个分布式锁,

客户端1申请加锁,加锁成功:

127.0.0.1:6379> SETNX lock 1
(integer) 1 //客户端1,加锁成功

客户端2申请加锁,因为它后到达,加锁失败:

127.0.0.1:6379> SETNX lock 1
(integer) 0 //客户端2,加锁失败

此时,加锁成功的客户端;就可以去操作【共享资源】,例如,修改MYSQL的某一行数据,或者调用一个API请求。
操作完成后,还要及时的释放锁,给后来者让出操作共享资源的机会,如何释放锁呢?
直接使用DEL命令删除这个KEY即可:

127.0.0.1:6379> DEL lock 
(integer) 1 

但是,它存在一个很大的问题,当客户端1拿到锁之后,如果发生下面的场景,就会造成【死锁】:

1、程序处理业务逻辑异常,没有及时释放锁
2、进程挂了,没机会释放锁

这时,这个客户端就会一直占用这个锁,而其他的客户端就【永远】拿不到这把锁了。

怎么解决这个问题呢?

如何避免死锁?

我们很容易想到的方案就是,在申请锁的时候,就给设置一个【租期】。
在Redis中实现时,就是给 这个KEY设置一个【过期时间】。这里我们假设,操作共享资源的时间不超过10S,那么在给这个key设置10S过期即可:

127.0.0.1:6379> SETNX lock 1
(integer) 1 
127.0.0.1:6379> EXPIRE lock 10
(integer) 1 

这样一来,无论客户端是否异常,这个锁都可以在10S后【自动释放】,其他客户端依旧可以拿到锁。
但这样真的就没有问题吗?
还是有问题。
现在的操作,加锁,设置过期时间是2条命令,有没有可能只执行了第一条,第二条却【来不及】执行的情况发生呢?例如:
1、SETNX执行成功,执行EXPIRE时由于网络问题,执行失败。
2、SETNX执行成功,Redis异常宕机,Expire没有机会执行。
3、SETNX执行成功,客户端异常奔溃,Expire没有机会执行。

总之,这两条命令不能保证是原子操作(一起成功),就有潜在的风险倒置过期时间设置失败,那就依旧会发生【死锁】问题。

怎么办?

在Redis 2.6.12 之后,Redis扩展了Set命令的参数,用下面这一条命令就可以了:

127.0.0.1:6379> SET lock 1 EX 10 NX
OK

这就解决了死锁问题ÿ

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值