基于 Redis 实现简单的分布式锁

2016-5-22 | 发布者:数据库开发

(点击上方公众号,可快速关注)


来源:伯乐在线专栏作者 - 陆晨

链接:http://blog.jobbole.com/101295/

点击 → 了解如何加入专栏作者


摘要


分布式锁在很多应用场景下是非常有效的手段,比如当运行在多个机器上的不同进程需要访问同一个竞争资源的时候,那么就会涉及到进程对资源的加锁和释放,这样才能保证数据的安全访问。分布式锁实现的方案有很多,比如基于ZooKeeper实现、或者基于Mysql实现等等,今天我们来一起看看如何基于Redis实现分布式锁服务。


分布式锁要点


对于分布式锁的目标,我们必须首先明确三点:


  1. 任何一个时间点必须只能够有一个客户端拥有锁。


  2. 不能够有死锁,也就是最终客户端都能够获得锁,尽管可能会经历失败。


  3. 错误容忍性要好,只要有大部分的Redis实例存活,客户端就应该能够获得锁。


一种简单的方法


理解了上面我们列出的三个点,我们来分析一下一般的基于Redis实现的分布式锁:


使用Redis实现锁最简单的办法是创建一个key,且这个key通常有有限的存活时间,这一点可以利用Redis的过期时间特性,所以锁最终会被释放掉,当客户端需要释放资源的时候,客户端delete这个key即可。


So far so good!但是有个单点问题,假如Redis master挂掉怎么办,因此我们需要加个slave,当master挂掉的时候可以切换到slave。这又带来了新的问题,由于Redis的复制是异步的,因此我们不能保证同时只有一个客户端获得锁。


这个模型有很显然的竞态:


  1. Client在Master上面获得了锁。

  2. master在数据同步到slave之前挂掉了。

  3. slave升级成为master。

  4. Client B申请了同样的资源的锁,成功了!


在特定条件下这种情况是会发生的,当出现多个客户端同时获得锁的时候,我们就认为可以这种锁方案是不可靠的。


基于Redis单例的实现


为了后面更好的了解分布式锁的实现,我们先来看看如何基于Redis单例实现锁服务。我们可以用下面方法获得锁:


SET resource_name my_random_value NX PX 30000


上面的命令在只有当key不存在的时候会执行成功(NX选项),同时会设置过期时间为30000ms(PX选项)。key的值会被设置为my_random_value。这个值在多个客户端和锁中必须是唯一的,我们使用random value是为了方便安全地释放锁,看看下面的脚本:


if redis.call("get",KEYS[1]) == ARGV[1] then

    return redis.call("del",KEYS[1])

else

    return 0

end


只有当key存在且值是预期的值的时候才会删除key。这种方式可以避免误删除其他客户端创建的锁。例如,当客户端获取锁之后执行一个很长时间的逻辑,一直过了锁的过期时间,这个时候锁会被自动释放掉,而另外一个客户端又获取了这个锁,前一个客户端终于执行完了逻辑执行,回头释放锁,删除key,其实这个时候释放的已经是另外一个客户端持有的锁了。使用DEL是不安全的,因为客户端有可能误删其他客户端持有的锁。上面脚本的方法的好处是每次获得锁的时候加上一个随机的签名,当释放锁的时候去看看是不是自己持有的锁,这个时候就不会误删。


现在我们学会了如何在Redis单例上获取锁和释放锁,那么接下来我们看看如何在Redis集群上获取锁和释放锁。


基于Redlock算法的实现


在分布式环境下,假设我们有N个master,这些节点都是独立的,因此我们没有配置复制策略。上面我们已经学会了如何在单机环境下获取锁和释放锁,我们假设的更具体一些,N=5,为了能获取锁,客户端的步骤为:


  1. 获取当前系统的时间,以毫秒为单位。


  2. 顺序的获取N个Redis实例上的锁,在每个实例中都用同样的key和value。在步骤2中,客户端需要一个比过期时间小很多的超时时间,例如,如果自动过期时间为10s,那么超时时间大概是5~50ms,这样可以避免客户端一直被阻塞,而不能继续请求下一个实例。


  3. 客户端每次都要计算已经过去了多长时间,使用的时间是否小于key自动过期的时间同时又获取了至少3个实例的锁。如果是,那么我们认为客户端此次获取锁成功。


  4. 如果锁被获取了,锁的过期时间必须要减去获取锁花费的时间。


  5. 如果当前客户端获取锁失败,客户端需要释放所有之前获取到的锁。


总结


这篇文章主要介绍Redis实现分布式锁的基本方法,然后分别介绍通过Redis单例和Redis集群实现分布式锁的方法。



专栏作者简介 点击 → 加入专栏作者 )


陆晨:开源技术爱好者,中间件技术狂热者。

 

打赏支持作者写出更多好文章,谢谢!




今日微信公号推荐↓】

更多推荐请看值得关注的技术和设计公众号


其中推荐了包括技术设计极客 和 IT相亲相关的热门公众号。技术涵盖:Python、Web前端、Java、安卓、iOS、PHP、C/C++、.NET、Linux、数据库、运维、大数据、算法、IT职场等。点击《值得关注的技术和设计公众号》,发现精彩!



热门文章

更多

点赞党

慧凡正能量:心中有尺,口中有度!

慧凡正能量:心中有尺,口中有度!

一句无心的话,或许引起一场纷争;一句善意的话,或许能让人心生美好;培植一个人需要千言万语;毁灭一个人只需要一句话。谨慎出言,才能少生事端;三思量行,才会少生遗憾。一句话,很轻,也很重;一句话,很普...
ihuifan • 
有一种幸福叫父母在,爸爸妈妈平安幸福,健康长...

有一种幸福叫父母在,爸爸妈妈平安幸福,健康长...

有一种幸福叫做 上有老,父母在。父母在,家才是你安魂入梦的地方。回到家来,亲热地叫一声爸爸妈妈,我们才能充分感知一个家的温馨和踏实。家有老人,就意味着这个世界上永恒的亲情还在。工作和事业失败了,可...
国学艺术精粹 • 
[正能量]一个年轻人去买碗,老板的一句话却改变...

[正能量]一个年轻人去买碗,老板的一句话却改变...

有一个年轻人去买碗,来到店里他顺手拿起一只碗,然后依次与其它碗轻轻碰击,碗与碗之间相碰时立即发出沉闷、浑浊的声响,他失望地摇摇头。然后去试下一只碗。。。他几乎挑遍了店里所有的碗,竟然没有一只满意的...
中国成功人士 • 
很多朋友都是从此绝交的..故事很短,说穿了人性...

很多朋友都是从此绝交的..故事很短,说穿了人性...

1甲不喜欢吃鸡蛋,每次发了鸡蛋都给乙吃。刚开始乙很感谢,久而久之便习惯了。习惯了,便理所当然了。于是,直到有一天,甲将鸡蛋给了丙,乙就不爽了。她忘记了这个鸡蛋本来就是甲的,甲想给谁都可以。为此,她...
触动心灵的美文 • 
涨姿势女票在床上为啥都不想动?

涨姿势女票在床上为啥都不想动?

总感觉每次战斗中少了点什么?甚至觉得自己像在守活寡?每次嘿嘿就像是走走场虽然你释放了但你根本没有高潮战斗前你会幻想各种很棒的场景但现实却相差甚远女票只是躺在那儿,因为她觉得只要这样就可以了;你要的...
她他社 • 
加载更多

 更多点赞党