基于Redis的分布式锁到底安全吗?

代码星冰乐

专注成就未来

首页 归档 关于

基于Redis的分布式锁到底安全吗?

Feb 10, 2020 | haifeiWu | Java | 阅读
文章目录
  1. 1. 单机 Redis 实现的分布式锁
  2. 2. 分布式 Redis 实现的分布式锁
    1. 2.1. Redlock 算法
    2. 2.2. DDIA 作者对于 Redlock 的观点
  3. 3. 最后的讨论
  4. 4. 参考

作 者:haifeiWu
原文链接:https://www.hchstudio.cn/article/2020/7bc5/
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。


由于版权原因,请阅读原文 --> 基于Redis的分布式锁到底安全吗?

关注我们

作 者:haifeiWu
原文链接:https://www.hchstudio.cn/article/2020/7bc5/
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。

作 者:haifeiWu
原文链接:https://www.hchstudio.cn/article/2020/7bc5/
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。

单机 Redis 实现的分布式锁

1,单机实现分布式锁的脚本(官方推荐实现)

1
2
3
4
5
6
7
SET lock_key random_value NX PX 10000
// do sth
eval "if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end"

2,注意事项(对释放锁的控制,以及锁超时的控制)random_value 要保证唯一,可以用 trace_id 来保证!
3,存在的问题,单机Redis只是依赖单台 Redis ,当依赖的 Redis 挂掉之后会造成比较大的问题!
4,那么部署 Redis 的主从可以保证吗?主要原因是 Redis 主节点与从节点之间的数据同步是异步的。

分布式 Redis 实现的分布式锁

Redlock 算法

Redlock 算法是基于 N 个完全独立的 Redis 节点(通常情况下 N 可以设置成 5)。
1,获取当前时间(毫秒数)。

2,按顺序依次向 N 个 Redis 节点执行获取锁的操作。这个获取操作跟基于单 Redis 节点的获取锁的过程相同。为了保证在某个 Redis 节点不可用的时候算法能够继续运行,这个获取锁的操作还有一个超时时间(time out),它要远小于锁的有效时间(几十毫秒量级)。客户端在向某个 Redis 节点获取锁失败(比如该Redis节点不可用,或者该 Redis 节点上的锁已经被其它客户端持有)以后,应该立即尝试下一个 Redis 节点。

3,计算整个获取锁的过程总共消耗了多长时间,计算方法是用当前时间减去第1步记录的时间。如果客户端从大多数Redis节点(>= N/2+1)成功获取到了锁,并且获取锁总共消耗的时间没有超过锁的有效时间(lock validity time),那么这时客户端才认为最终获取锁成功;否则,认为最终获取锁失败。

4,如果最终获取锁成功了,那么这个锁的有效时间应该重新计算,它等于最初的锁的有效时间减去第3步计算出来的获取锁消耗的时间。

5,如果最终获取锁失败了(可能由于获取到锁的 Redis 节点个数少于 N/2+1 ,或者整个获取锁的过程消耗的时间超过了锁的最初有效时间),那么客户端应该立即向所有 Redis 节点发起释放锁的操作(这里来保证所有的 Redis 节点都可以报以获取的锁释放掉)。

Redlock 算法实现的前提是基于不同的机器具有相同的时钟,或者误差很小可以忽略不计的假设。(这也是DDIA作者喷的一点)

存在的问题:
1,关于Redis的持久化的问题
假设一共有5个Redis节点:A, B, C, D, E。设想发生了如下的事件序列:
1.1 客户端1成功锁住了A, B, C,获取锁成功(但D和E没有锁住)。
1.2 节点C崩溃重启了,但客户端1在C上加的锁没有持久化下来,丢失了。
1.3 节点C重启后,客户端2锁住了C, D, E,获取锁成功。

Redis 给出的解决方案:延迟重启,既当 Redis 节点挂掉之后不要立刻重启,而要等待一个锁的过期时间之后再重启。

2,如果获取锁消耗的时间过多以至于无法完成后续的操作,如何释放锁?
个人认为需要业务方自己拿捏一个业务操作的需要消耗的时长,

3,antirez在设计Redlock的时候,是充分考虑了网络延迟和程序停顿所带来的影响的。但是,对于客户端和资源服务器之间的延迟(即发生在算法第3步之后的延迟),antirez 是承认所有的分布式锁的实现,包括 Redlock,是没有什么好办法来应对的。

DDIA 作者对于 Redlock 的观点

在 Martin 的这篇文章中,他把锁的用途分为两种:

  • 为了效率(efficiency),协调各个客户端避免做重复的工作。即使锁偶尔失效了,只是可能把某些操作多做一遍而已,不会产生其它的不良后果。比如重复发送了一封同样的 email。
  • 为了正确性(correctness)。在任何情况下都不允许锁失效的情况发生,因为一旦发生,就可能意味着数据不一致(inconsistency),数据丢失,文件损坏,或者其它严重的问题。

1,带有自动过期功能的分布式锁,必须提供某种fencing机制来保证对共享资源的真正的互斥保护。Redlock 提供不了这样一种机制。

上图展示的是:由于GC停顿造成的共享资源被多个客户端访问的问题。原因是:客户端在 GC 停顿造成锁的等待超时从而被释放,然而客户端并不知道,认为自己还是处于持有锁的状态。


上图展示的是:通过使用 fencing tokens 来解决锁失效未释放的问题。Redis 指出上图的漏洞,如果客户端1和客户端2都发生了GC pause,两个fencing token都延迟了,它们几乎同时到达了资源服务器,但保持了顺序,那么资源服务器是不是就检查不出问题了?这时对于资源的访问是不是就发生冲突了?

2,Redlock 构建在一个不够安全的系统模型之上。它对于系统的记时假设(timing assumption)有比较强的要求,而这些要求在现实的系统中是无法保证的。
Redlock 算法对机器时钟的强依赖,Martin 认为算法的实现不应该对时序做任何假设:进程可能会暂停任意时长,数据包可能会在网络中被任意延迟,时钟可能会被任意错误,即便如此,该算法仍可以正确执行并给出正确结果。
关于时钟的不可靠性:Redis 作者认为 Redlock 对时钟的要求,并不需要完全精确,它只需要时钟差不多精确就可以了。

3,在 Redlock 第三步完成之后的网络延迟,也为造成 Redlock 算法的失效,但是这个问题并不是 Redlock 算法独有的

Martin得出了如下的结论:

  • 如果是为了效率(efficiency)而使用分布式锁,允许锁的偶尔失效,那么使用单 Redis 节点的锁方案就足够了,简单而且效率高。Redlock 则是个过重的实现(heavy weight)。
  • 如果是为了正确性(correctness)在很严肃的场合使用分布式锁,那么不要使用 Redlock。它不是建立在异步模型上的一个足够强的算法,它对于系统模型的假设中包含很多危险的成分(对于 timing)。而且,它没有一个机制能够提供 fencing token。那应该使用什么技术呢?Martin认为,应该考虑类似Zookeeper的方案,或者支持事务的数据库。

最后的讨论

在了解了 Redlock 算法的逻辑及其可能存在的问题之后,我们可以针对自己的业务场景进行选择~

参考

How to do distributed locking

Is Redlock safe?

Distributed locks with Redis

关注我们

作 者:haifeiWu
原文链接:https://www.hchstudio.cn/article/2020/7bc5/
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。

分享
Zookeeper 与分布式锁【译】Raft 学生指南
微信关注我们
分类
  • Android8
  • Go4
  • Java59
  • Kafka,Java1
  • Kotlin2
  • Linux1
  • MapReduce1
  • Python2
  • Raft1
  • Redis1
  • ThreadPoolExecutor1
  • go1
  • 工具1
  • 总结8
  • 旅游日记1
标签
Nginx ChanghuiN haifeiWu Android Java 设计模式 hexo Kotlin 算法 MySQL 源码解析 Python Redis golang web Kafka 配置中心 总结 性能优化 旅游日记 Shell Go 问题排查 译文 Docker Spring Boot 工具 学习笔记 WebFlux 性能测试 go 散列表 源码 netty Raft
最近文章
  • Kafka的日志复制机制
  • 从20到21
  • go 并发编程
  • 【译】了解Linux CPU负载-您何时应该担心?
  • Zookeeper 与分布式锁
  • 基于Redis的分布式锁到底安全吗?
  • 【译】Raft 学生指南
  • ThreadPoolExecutor 的简单梳理
  • MapReduce 的简单实现
  • 使用 Map 实现策略模式
福利专区
    免费SSL证书
      阿里云红包
        腾讯云专属福利
        Copyright © 2021 代码星冰乐. Powered by ChanghuiN. 版权所有 晋ICP备15001365号
        特别感谢: 云服务器服务商 、 CDN 服务商