1.应用场景

主要用于学习和使用Redis实现分布式锁~

2.学习/操作

后续做成思维导图

1.文档阅读

2021-04-24 - 分布式锁(含Redis来实现) - 学习/实践_穿素白衫的少年的博客-CSDN博客

30 | 如何使用Redis实现分布式锁?-极客时间

深度剖析:Redis分布式锁到底安全吗?看完这篇文章彻底懂了!

2.整理输出

Redis加锁的主要用途是在多线程或多进程环境下对共享资源进行访问控制,避免多个线程或进程同时访问同一个共享资源而出现问题。

在Redis中,可以使用SET命令对某个key进行加锁,只有拥有锁的客户端才能对这个key进行操作,其它客户端需要等待锁的释放才能访问它。

加锁的方式有多种,常用的是使用SET命令设置锁的key,并设置过期时间,如果锁被占用,则其它客户端设置锁时会失败。

关于PHP实现的Redis实现的锁,参考:

learning_of_php/功能模块实现/分布式锁/redis/RedisService.php

原文

后续补充

...

3.问题/补充

 1. Kaito
是否可以使用 SETNX + EXPIRE 来完成加锁操作?

30 | 如何使用Redis实现分布式锁?-极客时间

不可以这么使用。使用 2 个命令无法保证操作的原子性,在异常情况下,加锁结果会不符合预期。异常情况主要分为以下几种情况:

1、SETNX 执行成功,执行 EXPIRE 时由于网络问题设置过期失败

2、SETNX 执行成功,此时 Redis 实例宕机,EXPIRE 没有机会执行

3、SETNX 执行成功,客户端异常崩溃,EXPIRE 没有机会执行

如果发生以上情况,并且客户端在释放锁时发生异常,没有正常释放锁,那么这把锁就会一直无法释放,其他线程都无法再获得锁。

下面说一下关于 Redis 分布式锁可靠性的问题。

使用单个 Redis 节点(只有一个master)使用分布锁,如果实例宕机,那么无法进行锁操作了。那么采用主从集群模式部署是否可以保证锁的可靠性?

答案是也很难保证。如果在 master 上加锁成功,此时 master 宕机,由于主从复制是异步的,加锁操作的命令还未同步到 slave,此时主从切换,新 master 节点依旧会丢失该锁,对业务来说相当于锁失效了。

所以 Redis 作者才提出基于多个 Redis 节点(master节点)的 Redlock 算法,但这个算法涉及的细节很多,作者在提出这个算法时,业界的分布式系统专家还与 Redis 作者发生过一场争论,来评估这个算法的可靠性,争论的细节都是关于异常情况可能导致 Redlock 失效的场景,例如加锁过程中客户端发生了阻塞、机器时钟发生跳跃等等。

感兴趣的可以看下这篇文章,详细介绍了争论的细节,以及 Redis 分布式锁在各种异常情况是否安全的分析,收益会非常大:http://zhangtielei.com/posts/blog-redlock-reasoning.html。

简单总结,基于 Redis 使用分布锁的注意点:

1、使用 SET $lock_key $unique_val EX $second NX 命令保证加锁原子性,并为锁设置过期时间

2、锁的过期时间要提前评估好,要大于操作共享资源的时间

3、每个线程加锁时设置随机值,释放锁时判断是否和加锁设置的值一致,防止自己的锁被别人释放

4、释放锁时使用 Lua 脚本,保证操作的原子性

5、基于多个节点的 Redlock,加锁时超过半数节点操作成功,并且获取锁的耗时没有超过锁的有效时间才算加锁成功

6、Redlock 释放锁时,要对所有节点释放(即使某个节点加锁失败了),因为加锁时可能发生服务端加锁成功,由于网络问题,给客户端回复网络包失败的情况,所以需要把所有节点可能存的锁都释放掉

7、使用 Redlock 时要避免机器时钟发生跳跃,需要运维来保证,对运维有一定要求,否则可能会导致 Redlock 失效。例如共 3 个节点,线程 A 操作 2 个节点加锁成功,但其中 1 个节点机器时钟发生跳跃,锁提前过期,线程 B 正好在另外 2 个节点也加锁成功,此时 Redlock 相当于失效了(Redis 作者和分布式系统专家争论的重要点就在这)

8、如果为了效率,使用基于单个 Redis 节点的分布式锁即可,此方案缺点是允许锁偶尔失效,优点是简单效率高

9、如果是为了正确性,业务对于结果要求非常严格,建议使用 Redlock,但缺点是使用比较重,部署成本高
2020-10-28

网友补充

凯文小猪

这里老师漏了一点 就是session timeout处理 在分布式锁的场景中就是: 一个key过期了 但是代码还没处理完 此时就发生了重复加锁的问题。 通常我们有两种方式处理: 1. 设置看门狗 也就是redision的处理方式 2. 设置状态机 由最后的业务层来做代码回溯

2.Darren

其实分布式锁选择Etcd的话,可能会更好。

Etcd 支持以下功能,正是依赖这些功能来实现分布式锁的:

1、Lease 即租约机制(TTL,Time To Live)机制:

Etcd 可以为存储的 KV 对设置租约,当租约到期,KV 将失效删除;同时也支持续约,即 KeepAlive;

Redis在这方面很难实现,一般假设通过SETNX设置的时间10S,如果发生网络抖动,万一业务执行超过10S,此时别的线程就能回去到锁;


2、Revision 机制:

Etcd 每个 key 带有一个 Revision 属性值,每进行一次事务对应的全局 Revision 值都会加一,因此每个 key 对应的 Revision 属性值都是全局唯一的。通过比较 Revision 的大小就可以知道进行写操作的顺序。 在实现分布式锁时,多个程序同时抢锁,根据 Revision 值大小依次获得锁,可以避免 “惊群效应”,实现公平锁;

Redis很难实现公平锁,而且在某些情况下,也会产生 “惊群效应”;
 

3、Prefix 即前缀机制,也称目录机制:

Etcd 可以根据前缀(目录)获取该目录下所有的 key 及对应的属性(包括 key, value 以及 revision 等);

Redis也可以的,使用keys命令或者scan,生产环境一定要使用scan;


4、Watch 机制:

Etcd Watch 机制支持 Watch 某个固定的 key,也支持 Watch 一个目录(前缀机制),当被 Watch 的 key 或目录发生变化,客户端将收到通知;

Redis只能通过客户端定时轮训的形式去判断key是否存在;

网友评论

4.参考

TBD

后续补充

...

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐