实现分布式锁的解决方案中,你认为Zookeeper和Redis哪种解决方案更好
Redis可以使用SetNX这个指令去实现分布式锁。
Zookeeper他可以用那个同一个节点的唯一性或者有序节点的这样的一个特性去实现分布式锁。
Redis的读写性能比Zookeeper更好。所以在高并发的一些场景中,就是Zookeeper在实现分布式锁上还是会有些瓶颈的。
所以我认为Redis要比Zookeeper更好。
为什么要使用分布式锁
使用分布式锁的一个核心目的是为了解决在同一个时刻有多个进程或者多个线程去访问某个共享资源所带来的一个安全性问题。
而根据锁的用途,我们可以把它分为两类。
共享锁
第一类是属于共享锁,所谓的共享锁就是说我们在同一个时刻允许多个进程或者线程去访问某个共享资源。
这样的一个核心点是可以适用在幂等性的一个场景中,从而去避免重复加锁,所带来的一个性能开销。
排他锁
排他锁就是我在同一个时刻,只允许一个进程或者线程去访问这个共享资源。
这个比较适合在非幂等性场景中。也就是说我们需要去保证在同一个时刻只有一个进程或者线程去访问这个共享资源。
目前实现分布式锁的两种常用中间件
目前去实现分布式锁最常用的中间件有Redis和Zookeeper。
基于Redis
Redis可以通过两种方式来实现。
基于Redis里面的SET NX
第一种是基于Redis的SET NX这样的一个指令。
SET key value NX PX milliseconds
这个指令有一个特殊的点就是我们往里面去设置一个key的时候,如果发现这个key已经存在于Redis的服务器上,他会返回一个0,表示我当前无法设置。否则的话,返回个1表示成功。那么我们的程序可以根据0和1来判断当前的这样一个状态,从而去表示获得锁。
基于Redission
第二个基于Redission这样一个客户端来实现分布式锁。Redission提供了分布式锁的一个封装方法。
我们只需要调用里面封装好的api,lock()和unlock()方法就可以去实现锁的抢占和释放。
而Redission里面所有的指令都是通过lua脚本去实现的。而lua脚本它可以保证我们执行所有指令的一个原子性。
redissionn里面提供了一个叫watchdog,翻译过来就是看门狗,他会在你获取锁之后,每隔10秒钟去帮你把你的key的时间做一个续约,从而去避免你的锁的过期。
基于Zookeeper来实现分布式锁
Zookeeper去实现分布式锁的方法比较多。
使用有序节点来实现
我们可以使用有序节点来实现。每个线程或者进程都到Zookeeper上的‘/lock’的目录下去创建一个临时有序节点去表示抢占锁。
所有创建的节点都会按照先后顺序生成一个带有有序编号的节点。线程创建节点之后,获取‘/lock’节点下的所有子节点去判断当前线程创建的节点是否是所有子节点里面序号最小的。
如果当前线程所创建的节点是所有子节点中最小的节点,那么我们认为他是获取锁成功。如果当前线程创建的节点不是最小的,我们需要去对当前线程的前一个节点去建立一个事件监听。
当被监听这个节点被释放之后,则触发一个回调告诉当前线程,从而再次去尝试抢占锁。这就是Zookeeper去基于有序节点实现分布式锁的一个实现原理。
各自的优缺点
Redis
缺点
- 他在获得锁的时候,他非常简单粗暴,如果取不到锁,他就会不断去尝试。这就会对我们整个应用程序的一个性能会造成很大的一个影响。
- Redis是一个AP模型,也就是我的一个可用性模型。那么在集群模式中,很容易存在数据一致性会导致锁出现问题。即便是我们使用redlock这种算法来实现分布式锁。但是在某些复杂的场景下,也无法去保证锁的百分之百可用。
【补充】:
AP模型(可用性和分区容忍性模型):它强调在面对网络分区的情况下保证系统的可用性,即允许部分节点独立工作而不要全局一致性。
实际开发中效果
不过在实际开发中,我们就使用Redis来实现分布式锁还是比较常见的。
因为大部分情况下,都不会遇到一些极端复杂的一些场景。
最重要的是,Redis性能是比较高的。所以在高并发场景中,他会比较合适一些。
Zookeeper
Zookeeper天生的设计定位就是一个分布的协调组件。他是一个CP模型。所以他更适合用来去实现分布式锁。
对于Zookeeper来说他如果获取不到锁,只需要添加一个监听器即可。不用一直去轮询,所以性能消耗会比较小。
【补充】:
CP模型(一致性和分区容忍性模型):意味着在面对网络分区的情况下,它会选择保证一致性而牺牲可用性。
两者之间的选择
如果在两者之间一定要做选择的话,会更推荐使用ZK来实现分布式锁。
因为对于分布锁来说,他是一个CP模型的场景,而Redis是一个AP模型的场景。
所以Zookeeper更加适合一点。