Redis的分布式锁

160 阅读2分钟

关于Redis的分布式锁

日前在网上浏览关于redis分布式锁的信息,发现了一些实现的些许瑕疵,故来讨论。

  1. 问题
    • 大部分博客资料都指明用redis的setnx(),然后再设置过期时间来实现。但是问题在于这并非原子操作。
    • 释放锁的问题同上。
    • 释放锁的另外一个问题是没有保证该锁只能被持有线程释放,即其他线程仍然可以删除该锁。

  1. 解决问题
    • redis分布式锁的实现方式,其实redis官方文档上有明确推荐的,setnx()操作已经被在set操作中有了相应实现,在官方文档中指出未来可能被废弃,详细见官方文档
      1. 所以应当采取的方案是使用set命令来获取锁并设置过期时间,并且为原子操作。SET key value [expiration EX seconds|PX milliseconds] [NX|XX],此方法成功获得锁的返回值是"OK".
      2. spring中封装的redisTemplate没有上述get命令的实现,可以用放射的方式获取jedis来操作,或者直接使用jedis。
    • 保证锁只能被持有锁的线程释放
      1. set的value要保证唯一性,在释放锁的时候通过判断该value来确保只能被持有锁的线程释放。
    • 释放锁的原子性
      1. 使用lua脚本实现,这也是redis官方推荐的方式
      2. 具体的操作可以使用jedis封装的执行lua脚本命令实现,是原子操作。操作成功的返回值是1.

  1. 仍旧存在的问题
    • 线程获取锁后阻塞,时长超过了锁的过期时间。
    • redis集群master宕掉的情况,此时可能slave节点还未同步到锁,可以参考下图。
      avatar
      avatar
  • 以上问题redis提供了redlock的解决方案,可以详见官方文档,redisson有相关实现。对于redlock和redisson了解不深,此篇不聊。关于这个算法Martin和redis的作者antirez(网名)有过讨论,双方过招十分精彩,没有找到一手资料,有兴趣的同学可以自己搜索看下。
  • 个人认为如果对锁的要求比较高的话还是使用zk来实现比较好。