关于 Redis 分布式锁的那些事
一、背景
最近在学习 Redis 相关的知识,学到了 Redis 分布式锁的实现及应用。在这个过程中,遇到了一些疑问,本文主要记录针对这一过程中遇到的疑问以及分布式锁相关知识点的总结。
ps.网上关于 Redis 实现分布式锁的文章、博客、专栏等的可供参考的资料很多很全。
二、分布式锁
说到分布式锁,那么就会有如下几个问题:
1、分布式锁应用的场景有哪些?分布式锁是什么,应该具备哪些特点?
2、Redis 为什么适合做分布式锁?
3、使用 Redis 实现分布式锁,具体有哪些实现方法?
带着这几个问题,可以在网上找到很多答案,此处会对这几个问题做个简单的总结记录。
1、分布式锁应用的场景有哪些?分布式锁是什么,应该具备哪些特点?
锁的作用是保证多个进程或线程在并发操作操作共享资源时资源的正确性。在分布式应用中,一个服务需要部署多个实例,对于操作分布式环境下的共享资源,就需要使用分布式锁来保证操作的正确性。
分布式锁应该具有互斥、可重入、锁超时,高可用等特点。其中前面几个特点和本地锁具体的特点相同,高可用是分布式锁需要具备的重要的特点。
关于分布式锁的应用场景,找了下最近参与的一些项目,实际应用到分布式锁的不多,可以了解到的是类似库存扣减这类应用场景。
2、Redis 为什么适合做分布式锁?
高性能:Redis 读写性能高,可以应对高并发的锁操作场景
高可靠:Redis 是分布式系统,具备高可用方案
3、使用 Redis 实现分布式锁,具体有哪些实现方法?
使用 Redis 实现分布式锁应该具备满足两个条件,加锁和解锁过程必须是原子操作;保证高可用,使用 Redis Cluster 集群部署。
具体实现的方案包括,基于单个 Redis 节点实现与基于多个 Redis 节点实现的高可靠分布式锁。
三、Redis 实现分布式锁
基于单节点实现
线上的 Redis 部署一般都是集群模式,基于单节点实现,即在集群模式下只会对一个 master 进行加解锁操作,至于是哪个 master,则需要根据 Redis 的 key 计算出的哈希槽来决定(具体可以去了解 Redis Cluster 模式)。
加锁、解锁操作可能需要多个操作,需要保证操作的原子性,通过 Redis 的单命令和 Lua 脚本两种方式实现原子操作。
加锁
加锁注意事项
1、加锁过程要保证原子性
2、保证谁加的锁只能被谁解锁,即 Redis 加锁的 value,解锁时需要传入相同的 value 才能成功,保证 value 唯一性
3、设置锁超时时间,防止加锁方异常无法释放锁时其他客户端无法获取锁,同时,超时时间要大于业务处理时间
使用 Redis 命令 SET lock_key unique_value NX EX seconds
进行加锁,单命令操作,Redis 是串行执行命令,所以能保证只有一个能加锁成功。
解锁
解锁通过DEL
命令来删除,为了避免错误的解锁(A 加锁,B 解锁),所以需要比较 value,整个过程为了保证原子性,所以使用 Lua 脚本(unlock.script)
此实现方案是基于单个节点保存锁信息,不具备高可靠性。要保证高可靠,则要基于多个节点实现。
还有一个缺点,可能存在设置的过期时间已到,业务处理还未结束就提前释放锁,其他请求可以继续获取到锁。解决方案就是使用Redisson
,大致的解决原理是,开启一个守护线程定期检查锁是否存在,如存在则延长 key 的时间,尝试获取的客户端则通过自旋的方式获取锁。
https://github.com/redisson/redisson
基于多个节点实现
RedLock 算法
由 Redis 的开发者 Antirez 提出,RedLock 算法的基本思路是让客户端和多个独立的 Redis 实例依次请求加锁,如果能和半数以上的实例加锁成功,就可认为客户端获取分布式锁成功。
实现步骤如下:
1、客户端获取当前时间
2、客户端依次像 N 个 Redis 节点执行加锁操作:加锁操作与单价加锁一样,同样要设置过期时间,只是要加上 Redis 实例标识。
3、客户端完成对所有 Redis 节点的加锁操作,计算整个过程的总耗时,要同时满足:
(1)客户端对超过半数的实例加锁成功
(2)客户端获取锁的总耗时小于过期时间
如果没有同时满足这两个条件,则要向所有 Redis 节点执行释放锁的操作。
关于 RedLock 算法,业界的分布式系统专家Martin Kleppmann还与 Antirez
发生过一场争论,来评估这个算法的可靠性,争论的细节都是关于异常情况可能导致 RedLock 失效的场景,例如加锁过程中客户端发生了阻塞、机器时钟发生跳跃等等。(可见http://zhangtielei.com/posts/blog-redlock-reasoning.html)
总结
分布式系统设计是实现复杂性和收益的平衡,既要尽可能地安全可靠,也要避免过度设计。如果是为了正确性,业务对于结果要求非常严格,建议使用 RedLock,但缺点是使用比较重,部署成本高;如果为了效率,使用基于单个 Redis 节点的分布式锁即可,此方案缺点是允许锁偶尔失效,优点是简单效率高。大多数情况,选择基于单个 Redis 节点的分布式锁即可。
其他
在线 redis 工具:https://try.redis.io/,可以用于熟悉使用 redis 命令。
参考:https://www.infoq.cn/article/dvaaj71f4fbqsxmgvdce
版权声明: 本文为 InfoQ 作者【Hex】的原创文章。
原文链接:【http://xie.infoq.cn/article/8b68785d457cd82386ee1f6bc】。文章转载请联系作者。
评论