关于Redis分布式锁的那些事

Posted ningtalk

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于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)

// 解锁 比较unique_value是否相等
if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end
redis-cli  --eval  unlock.script lock_key , unique_value 

此实现方案是基于单个节点保存锁信息,不具备高可靠性。要保证高可靠,则要基于多个节点实现。

还有一个缺点,可能存在设置的过期时间已到,业务处理还未结束就提前释放锁,其他请求可以继续获取到锁。解决方案就是使用Redisson,大致的解决原理是,开启一个守护线程定期检查锁是否存在,如存在则延长key的时间,尝试获取的客户端则通过自旋的方式获取锁。

https://github.com/redisson/r...

基于多个节点实现

RedLock算法

由Redis的开发者Antirez提出,RedLock算法的基本思路是让客户端和多个独立的Redis实例依次请求加锁,如果能和半数以上的实例加锁成功,就可认为客户端获取分布式锁成功。

实现步骤如下:

  • 1、客户端获取当前时间
  • 2、客户端依次像N个Redis节点执行加锁操作:加锁操作与单价加锁一样,同样要设置过期时间,只是要加上Redis实例标识。
  • 3、客户端完成对所有Redis节点的加锁操作,计算整个过程的总耗时,要同时满足:

    * (1)客户端对超过半数的实例加锁成功
    * (2)客户端获取锁的总耗时小于过期时间
    

    如果没有同时满足这两个条件,则要向所有Redis节点执行释放锁的操作。

关于RedLock算法,业界的分布式系统专家Martin Kleppmann还与 Antirez发生过一场争论,来评估这个算法的可靠性,争论的细节都是关于异常情况可能导致 RedLock 失效的场景,例如加锁过程中客户端发生了阻塞、机器时钟发生跳跃等等。(可见http://zhangtielei.com/posts/...)

总结

分布式系统设计是实现复杂性和收益的平衡,既要尽可能地安全可靠,也要避免过度设计。如果是为了正确性,业务对于结果要求非常严格,建议使用 RedLock,但缺点是使用比较重,部署成本高;如果为了效率,使用基于单个 Redis 节点的分布式锁即可,此方案缺点是允许锁偶尔失效,优点是简单效率高。大多数情况,选择基于单个Redis节点的分布式锁即可。

其他

  • 在线redis工具:https://try.redis.io/,可以用于熟悉使用redis命令。
  • 参考:https://www.infoq.cn/article/...

以上是关于关于Redis分布式锁的那些事的主要内容,如果未能解决你的问题,请参考以下文章

Redis实现分布式锁(设计模式应用实战)

Redis实现分布式锁(设计模式应用实战)

关于redis分布式锁的实现方式(转载)

关于分布式锁的整理

关于Elasticsearch那些事

分布式套路之万字详解Redis/Redission分布式锁原理