欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

redis分布式锁实现原理_redis分布式锁实现分析与实践

发布时间:2025/3/20 编程问答 24 豆豆
生活随笔 收集整理的这篇文章主要介绍了 redis分布式锁实现原理_redis分布式锁实现分析与实践 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

前言: 在分布式环境中, 我们有些情况下需要使用到锁进行并发控制, 可供基于的 redis, zookeeper,mysql类数据库 基于数据库类的实现是乐观锁, 基于redis,zookeeper的实现可以认为是悲观锁.. 乐观锁与悲观锁最根本的区别是在于线程之间是否相互阻塞.

背景:一个订单,客户正在前台修改地址,管理员在后台同时修改备注。地址和备注字段的修改,都必须正确更新,这二个请求同时到达的话,如果不借助db的事务,很容易造成行锁竞争,但用事务的话,db的性能显然比不上redis轻量。

解决思路:A,B二个请求,谁先抢到分布式锁(假设A先抢到锁),谁先处理,抢不到的那个(即:B),在一旁不停等待重试,重试期间一旦发现获取锁成功,即表示A已经处理完,把锁释放了。这时B就可以继续处理了。

但有二点要注意:

a、需要设置等待重试的最长时间,否则如果A处理过程中有bug,一直卡死, 或宕机,或者未能正确释放锁,B就一直会等待重试,但是又永远拿不到锁。

b、等待最长时间,必须小于锁的过期时间。否则,假设锁2秒过期自动释放,但是A还没处理完(即:A的处理时间大于2秒),这时锁会因为redis key过期“提前”误释放,B重试时拿到锁,造成A,B同时处理。(注:可能有同学会说,不设置锁的过期时间,不就完了么?理论上讲,确实可以这么做,但是如果业务代码有bug,导致处理完后没有unlock,或者根本忘记了unlock,分布式锁就会一直无法释放。所以综合考虑,给分布式锁加一个“保底”的过期时间,让其始终有机会自动释放,更为靠谱)

那在设计上到底如何实现呢,直接上可用的:

从redis2.6.12版开始, redis为set命令增加(set [key] NX/XX EX/PX [expiration]);

  • EX seconds – 设置键key的过期时间,单位时秒
  • PX milliseconds – 设置键key的过期时间,单位时毫秒
  • NX – 只有键key不存在的时候才会设置key的值
  • XX – 只有键key存在的时候才会设置key的值
    set mylock nx ex 10 加锁
    为了防止死锁,redis至少设置一个过期时间,: 一旦设置过期时间就引申出来以下情况:
  • 当锁自动释放了,但是程序并没有执行完毕这个做发有二种: 1)加锁与释放锁控制中间逻辑, 使其在可控时间内执行完(这个有些废话了).2)在还没有释放的时候,给这个锁续上时间, 比如这次枷锁需要执行20分钟, 过期时间设置的10秒, 那就可以使用一个线程每过5秒检测一下这个锁是否被我这个进程枷锁,如果是并且检测出来过期时间小于5秒, 给这个锁续上10秒. 那引发了一个问题:怎么知道这个锁是否被本进程锁着呢,
    这时候value值派上用场了 set mylock my+threadID+随机数 nx ex 10 我在这里value使用 serverid + 进程ID+ 随机数 这样确保唯一性, 光使用进程ID会重的的
    这样做的好处在于:
    1) 每个获取redis锁的线程应该释放自己获取到的锁,而不是其他线程的.
    2) 在释放锁的时候需要判断当前锁是否属于自己,如果属于自己才释放,这里涉及到逻辑判断语句,至少是两个操作在进行,那么我们需要考虑这两个操作要在一个原子内执行,否者在两个行为之间可能会有其他线程插入执行,导致程序紊乱
    如果你这样做基本就可以满足一般的需求了.
SET

我们来分析一下:redlock

  • 更可靠的锁; 单实例的redis(这里指只有一个master节点)往往是不可靠的,虽然实现起来相对简单一些,但是会面临着宕机等不可用的场景,即使在主从复制的时候也显得并不可靠(因为redis的主从复制往往是异步的)。 文章分析得出,这种算法只需具备3个特性就可以实现一个最低保障的分布式锁。
  • 安全属性(Safety property): 独享(相互排斥)。在任意一个时刻,只有一个客户端持有锁。
  • 活性A(Liveness property A): 无死锁。即便持有锁的客户端崩溃(crashed)或者网络被分裂(gets partitioned),锁仍然可以被获取。
  • 活性B(Liveness property B): 容错。 只要大部分Redis节点都活着,客户端就可以获取和释放锁.

第一点安全属性意味着悲观锁(互斥锁)是我们做redis分布式锁的前提,否者将可能造成并发;

第二点表明为了避免死锁,我们需要设置锁超时时间,保证在一定的时间过后,锁可以重新被利用;

第三点是说对于客户端来说,获取锁和手动释放锁可以有更高的可靠性。

更进一步分析,结合上文提到的关键问题,这里可以引申出另外的两个问题:

  • 怎么才能合理判断程序真正处理的有效时间范围?(这里有个时间偏移的问题)
  • redis Master节点宕机后恢复(可能还没有持久化到磁盘)、主从节点切换,(N/2)+1这里的N应该怎么动态计算更合理?

接下来再看,redis作者对redlock的解说:

总结

以上是生活随笔为你收集整理的redis分布式锁实现原理_redis分布式锁实现分析与实践的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。