分布式锁通俗解读( 二 )


简化的结构图如下:
 

分布式锁通俗解读

文章插图
 
三、分布式锁的实现有哪些?
说到分布式锁的实现 , 还是有很多的 , 有数据库方式的 , 有redis分布式锁 , 有Zookeeper分布式锁等等 。
我们如果采用Redis作为分布式锁 , 那么上图中负“责红包的妹子(服务)” , 就可以替换成Redis , 请自行脑补 。
1、为什么Redis可以实现分布式锁?
首先Redis是单线程的 , 这里的单线程指的是网络请求模块使用了一个线程(所以不需考虑并发安全性) , 即一个线程处理所有网络请求 , 其他模块仍用了多个线程 。
在实际的操作中过程大致是这样子的:
服务器1要去访问发红包的妹子 , 也就是Redis , 那么他会在Redis中通过"setnx key value" 操作设置一个key进去 , value是啥不重要 , 重要的是要有一个key , 也就是一个标记 , 而且这个key你爱叫啥叫啥 , 只要所有的服务器设置的key相同就可以 。
假设我们设置一个 , 如下图:
 
分布式锁通俗解读

文章插图
 
那么我们可以看到会返回一个1 , 那就代表了成功 。
如果再来一个请求去设置同样的key , 如下图:
 
分布式锁通俗解读

文章插图
 
这个时候会返回0 , 那就代表失败了 。
那么我们就可以通过这个操作去判断是不是当前可以拿到锁 , 或者说可以去访问“负责发红包的妹子” , 如果返回1 , 那我就开始去执行后面的逻辑 , 如果返回0 , 那就说明已经被人占用了 , 我就要继续等待 。
当服务器1拿到锁之后 , 进行了业务处理 , 完成后 , 还需要释放锁 , 如下图所示:
 
分布式锁通俗解读

文章插图
 
删除成功返回1 , 那么其他的服务器就可以继续重复上面的步骤去设置这个key , 以达到获取锁的目的 。
当然以上的操作是在Redis客户端直接进行的 , 通过程序调用的话 , 肯定就不能这么写 , 比如java就需要通过jedis去调用 , 但是整个处理逻辑基本都是一样的 。
通过上面的方式 , 我们好像是解决了分布式锁的问题 , 但是想想还有没有什么问题呢?
对 , 问题还是有的 , 可能会有死锁的问题发生 , 比如服务器1设置完之后 , 获取了锁之后 , 忽然发生了宕机 。那后续的删除key操作就没法执行 , 这个key会一直在Redis中存在 , 其他服务器每次去检查 , 都会返回0 , 他们都会认为有人在使用锁 , 我需要等 。
为了解决这个死锁的问题 , 我们就需要给key设置有效期了 。
设置的方式有2种:
  • 第一种就是在set完key之后 , 直接设置key的有效期 "expire key timeout"  , 为key设置一个超时时间 , 单位为second , 超过这个时间锁会自动释放 , 避免死锁 。
这种方式相当于 , 把锁持有的有效期 , 交给了Redis去控制 。如果时间到了 , 你还没有给我删除key , 那Redis就直接给你删了 , 其他服务器就可以继续去setnx获取锁 。
  • 第二种方式 , 就是把删除key权利交给其他的服务器 , 那这个时候就需要用到value值了 , 比如服务器1 , 设置了value也就是timeout为当前时间+1秒  , 这个时候服务器2通过get发现时间已经超过系统当前时间了 , 那就说明服务器1没有释放锁 , 服务器1可能出问题了 , 服务器2就开始执行删除key操作 , 并且继续执行setnx操作 。
但是这块有一个问题 , 也就是不光你服务器2可能会发现服务器1超时了 , 服务器3也可能会发现 , 如果刚好服务器2 setnx操作完成 , 服务器3就接着删除 , 是不是服务器3也可以setnx成功了?


推荐阅读