分布式锁为什么要选择Zookeeper而不是Redis?
在分布式的应用中,为了防止单点故障,保障高可用,通常会采用主从结构,当主节点挂掉后,从节点可以代替主节点提供服务。
Redis通过复制 + sentinel哨兵来实现主从模式。
Zookeeper通过replicated mode复制模式来实现主从模式。
单从结构上看,Redis和Zookeeper都是主从架构,那Zookeeper的优势是什么?为什么要选择Zookeeper?难道只是因为Zookeeper是目录结构,Redis是K-V结构吗?
同步机制的不同
Redis
Redis在给从节点同步数据时,正常情况是增量同步,也就是主节点的数据修改语句(DML)会异步的同步给从节点。Redis的数据同步没有保障数据一致性的机制,也就是说,一条DML在主节点执行成功时,不能保障其他从节点成功执行了这条数据,这就会造成一个问题,如果在数据没有同步到从节点时,主节点挂掉,就会产生数据丢失的情况。
Zookeeper
Zookeeper使用类paxos算法(zab)来保障数据的一致性。简单的讲,当一个DML语句发送给主节点时,Zookeeper需要保证一半以上的节点接收到数据,才会返回成功。并且当主节点挂掉,从节点重新选举时,同步到最新的数据的节点会有优先选举权。
举个例子:
一个4节点Zookeeper(A、B、C、D),A是主节点,当执行一个create语句成功时,至少有3台节点执行成功(一半以上),例如A、C、D成功。此时如果A节点挂了,B、C、D进行选举,由于C、D都执行成功了create语句,B没有执行,C、D的数据更加新,具有优先选举权,再根据名称排序,选择C做为主节点。在整个选举过程中,服务不可用,选举完成后,C节点和A节点数据一致,不会出现丢失的情况。
分布式锁
要实现分布式锁,需要满足一些要求:
- 只能有一个服务的一个线程能获取锁
- 一个持有锁的线程挂掉后,锁应该被释放,用来给其他线程用
- 一个持有锁的线程没执行完,锁不能释放
- 锁释放后,其他等待者可以继续争抢
- 管理锁的主节点(Redis或Zookeeper)挂了,重新选举后,不影响锁的持有情况
Redis解决方案
问题1、问题2:使用“SET key value EX seconds NX”语句获取锁并设置过期时间
问题3:另开一个监控线程,监控主线程执行情况,用来延长过期时间
问题4:等待线程定时检查锁的持有情况
问题5:暂无或者解决成本很高,需要自己实现类paxos的算法
Zookeeper解决方案
通过创建临时节点可以解决问题1,2,3
watch机制可以解决问题4,并且相比定时检查,watch可以做到更高实时性
zookeeper的paxos同步机制保障了节点间数据一致性,即使主节点挂掉,也可以保障数据不丢,可以解决问题5
对比可以发现:
Zookeeper的机制可以保证分布式锁实现业务代码简单,成本低。
Redis如果要解决分布式锁的问题,对于一些复杂的情况,很难解决,成本较高。