Redis分布式锁

Redis锁在面试中是Redis绕不开的话题,关于Redis锁,网上很多文章,大多都是这个方案:

1、单机Redis

2、RedLock

3、Redission分布式锁

本文基于这三个点,延伸出几个问题,同时介绍下Martin和RedLock实现作者Salvatore的论点。

当然分布式锁并不只限于这两种,还有基于ZooKeeper的分布式锁的实现、Chubby的分布式锁、Mysql分布式锁、基于 Etcd、Hazelcast 分布式锁的等等,其中关于基于ZooKeeper的分布式锁的内容将放在下一次进行讲解。

一 单机Redis

单节点的分布式锁,网上已经有很多介绍了,很容易找到这样一句代码:

SET key value NX PX30000

如果上面的指令成功执行,那也就意味着获得了锁,此时可以对共享资源进行操作,如果指令执行失败,则意味着获取锁失败。

虽然这个指令非常简单,其中的几个问题还是值得思考的

1 px有必要吗。为什么?

首先想如果没有PX,会发生什么情况,假设一个进程A获得了这把锁,但是这个进程挂了,此时无法释放这把锁,那是不是这个进程就会一直持有这把锁呢?这样就导致其他进程无法获得共享资源。

所以需要这个PX对锁设置一个过期时间,防止其他进程无法获得锁。

思考另外一个问题,由于我们的进程运行在机器上,而机器运行的状态我们无法确定。进程A此时获得了锁,此时可能因为GC停顿导致了程序的卡顿,亦或是调用其他接口因为拥塞网络问题导致请求变慢,也许其他进程发送SIGSTOP信号,总之此时因为一些其他情况造成进程暂停,又有一个进程B获得了锁,如果此时进程A的过期时间到达,使用DEL删除这个记录,那么就会把进程B所拿到的锁进行删除,这样就会产生一个不安全的事故,所以PX的设置还是有必要的。

(超时后只有对key执行DEL命令或者SET命令或者GETSET时才会清除)

 

 

进程A的Stop-The-World GC时间结束,发现锁过期,反手DEL删除了锁,此时也就是删除了进程B所持有的锁。

 

那么如何解决这个问题呢,Martin给了一个解决方案,采用一个单调递增的数字来确定延迟到来的请求,其实也就相当于版本号,如果接收到低版本的请求,便拒绝就好了。

但是这样还是会有一个问题:如果因为两个客户端都发生了GC,但是版本号到达的顺序是正确的,那么是不是又有问题了?

2 过期时间设置多少合适,超时之后,共享资源是不是失去保护了呢?

Redis锁的过期时间设置,也是可以思考的点,例如Redis锁过期了,但是业务逻辑却没有执行完成,那该怎么办呢?

如果把Redis锁的过期时间再设置长一点怎么样?这样还是有问题,如果此时业务逻辑执行的非常快,过长的Redis锁过期时间的设置反而会降低效率。

如果此时客户端因为一些原因导致锁过期了,那么这里的客户端访问接下来的共享资源还安全吗?答案是否定的,共享资源此时已经失去了保护。

3 随机字符串有必要吗,为什么?

首先随机字符串是必要的,这保证了客户端释放所必须是自己所持有的锁。我们可以假设一个场景,如果这个字符串是一个固定不变的字符串,那么还是会产生类似问题一的结果:A所属的锁被B释放。因此这个随机字符串还是有必要的。

4 指令可以拆成两条指令实现吗,缺点有什么?

加锁的指令确实可以通过两条指令进行实现,例如:

SETNXkey valueEXPIRE key 30

这样做弊端很明显,就是没有原子操作,另外还有一个需要说的点,对于SETNX指令,在Redis官方文档上SET指令介绍有这样一句话:

 

 

大意是SET指令可以替换SETNX、SETEX、PS

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值