关于Redis的分布式锁
日前在网上浏览关于redis分布式锁的信息,发现了一些实现的些许瑕疵,故来讨论。
- 问题
- 大部分博客资料都指明用redis的setnx(),然后再设置过期时间来实现。但是问题在于这并非原子操作。
- 释放锁的问题同上。
- 释放锁的另外一个问题是没有保证该锁只能被持有线程释放,即其他线程仍然可以删除该锁。
- 解决问题
- redis分布式锁的实现方式,其实redis官方文档上有明确推荐的,setnx()操作已经被在set操作中有了相应实现,在官方文档中指出未来可能被废弃,详细见官方文档。
- 所以应当采取的方案是使用set命令来获取锁并设置过期时间,并且为原子操作。SET key value [expiration EX seconds|PX milliseconds] [NX|XX],此方法成功获得锁的返回值是"OK".
- spring中封装的redisTemplate没有上述get命令的实现,可以用放射的方式获取jedis来操作,或者直接使用jedis。
- 保证锁只能被持有锁的线程释放
- set的value要保证唯一性,在释放锁的时候通过判断该value来确保只能被持有锁的线程释放。
- 释放锁的原子性
- 使用lua脚本实现,这也是redis官方推荐的方式
- 具体的操作可以使用jedis封装的执行lua脚本命令实现,是原子操作。操作成功的返回值是1.
- redis分布式锁的实现方式,其实redis官方文档上有明确推荐的,setnx()操作已经被在set操作中有了相应实现,在官方文档中指出未来可能被废弃,详细见官方文档。
- 仍旧存在的问题
- 线程获取锁后阻塞,时长超过了锁的过期时间。
- redis集群master宕掉的情况,此时可能slave节点还未同步到锁,可以参考下图。
- 以上问题redis提供了redlock的解决方案,可以详见官方文档,redisson有相关实现。对于redlock和redisson了解不深,此篇不聊。关于这个算法Martin和redis的作者antirez(网名)有过讨论,双方过招十分精彩,没有找到一手资料,有兴趣的同学可以自己搜索看下。
- 个人认为如果对锁的要求比较高的话还是使用zk来实现比较好。