分布式锁之Redis锁和ZK锁

分布式锁

分布式系统中,常见的分布式锁有两种,一种是基于Redis实现的分布式锁,一种是基于ZooKeeper锁。本篇文章简要介绍下其原理及方案。redis

Redis锁

redis锁简单版本

上锁

先说上锁的命令,上锁的命令是:set {lockName} {randomVal} nx px 30000。spring

其中,nx 参数的意思是不存在锁的时候设置,px参数表示毫秒数,该条命令表示当不存在lockName键的时候,为其设置值为randomVal,并设置过时时间为30000毫秒。当redis中存在该键是redis返回nil。其余线程(包括其余机器)来获取锁的时候,能够用轮询来判断是否上锁成功,达到阻塞其余线程的目的。网络

解锁

解锁,咱们须要实现的需求是不能删除掉其余线程设置的锁。由于某些状况好比锁超时,其余线程仍是会获取到锁。因此必须先判断锁是否是本身设置的再进行删除,因为redis没有提供一个原子命令判断当前值是什么再进行删除,因此必须向redis传入lua脚本以确保解锁操做的原子性。解锁的原理是传入随机值进行解锁,脚本中会判断当前Key存不存在,存在的话再判断值是否与传入的随机值相等,相等则将其删除。架构

缺点

这种上锁方案有很明显的的缺点,如:并发

  • 一是这种方案并非很可靠,被上锁的redis宕机后容易丢数据,就算是配置了哨兵,也存在主备切换的时候可能丢数据。
  • 二是不能实现公平锁。
  • 三是轮询阻塞这种方式开销有点大。
  • 四是不可实现线程重入。
  • 没有续约机制

针对第一点,redis官方建议使用基于redisCluster的redlock(红锁)方案。这种方案核心要点就是须要在大多数redis节点上获取锁成功才算成功。但这就意味着开销变大了,而且针对红锁这种方案,网络上也有些大佬们提出质疑。框架

解决方案

这种简单版本的redis分布式锁方案并不能解决这些问题,若是要解决可使用redission框架,redission运用了队列、redis发布订阅机制,看门狗机制,较为复杂的加锁、释放锁脚本解决了这些问题。redission支持可重入锁、公平锁、红锁等,而且将redis锁按JDK的Lock接口进行了封装,操做简单易用。dom

ZK锁

简单版本的不公平ZK锁

上锁

以在ZooKeeper中成功建立临时节点为标识,建立成功则获取锁成功,建立失败则监听该节点,直到节点删除在尝试建立临时节点。 为何使用临时节点?避免服务宕机,致使死锁问题。分布式

解锁

删除节点即解锁成功。lua

方案缺点

这种方案的缺点是锁是不公平的,而且节点删除唤醒的其余监听线程比较多,效率没有接下来介绍的使用临时顺序节点的方案只唤醒下一个监听节点的方式高。线程

基于临时顺序节点的公平ZK锁

上锁

每次尝试获取锁都尝试建立一个临时顺序节点,而且获取当且父节点下的全部临时顺序节点,若是前面还有节点,则获取锁不成功,此时将主线程阻塞,监听前面一个节点被删除,若是被删除再唤醒主线程。反之若是当前建立的临时顺序节点前面没有节点则获取锁成功。

解锁

删除当前临时顺序节点即解锁成功。

解决的问题

这种方案实现的是公平锁,之前的并发竞争ZK临时节点建立,改成依次唤醒,下降了必定开销。

两种方案的对比

我的以为对于分布式系统来讲,redisCluster红锁的设计不是很优雅,感受基于zookeeper集群高可用的zk锁更优雅一些。因此若是作技术选型的话,我的倾向zk锁。可是若是技术架构中没有搭建zookeeper,可能选择的是springcloud那一套,选择redisssion封装的redis锁也行。

相关文章
相关标签/搜索