分布式锁可以使用Redis的setnx命令实现的,和下面的实现方法。

对setnx命令简介

命令格式


setnx关键值


当没有键不存在时,键的值被设置为值。



如果一个给定的键已经存在,setnx不做任何动作。



setnx是速记,如果不存在。

返回值

返回整数,特别是



- 1,当设置键的值时



- 0,当键的值未设置时

例子


redis > setnx MyKey你好
(整数)1
redis > setnx MyKey你好
(整数)0
redis >得到钥匙
你好
redis >


使用setnx实现分布式锁

多个进程执行以下redis:


setnx lock.foo


如果setnx返回1,表示工艺获取一个锁,setnx设置密钥lock.foo价值锁的超时时间(当前时间+锁的有效时间)。



如果setnx返回0,其他进程已锁定,和进程不能进入临界区的进程不断在循环中得到setnx锁试运行。

解决僵局

考虑一个案件。如果这个过程被锁,它断开与redis连接(过程或许是悬挂或网络中断)。如果没有有效的释放机制,所有其他进程都处于等待状态,即出现死锁。

当我们使用setnx获得锁上面,我们设置的关键lock.foo价值锁的有效时间。进程获取锁后,其他进程将不断检测锁是否已过期。如果时间不在,等待过程也有机会得到锁。

然而,当锁是随着时间的推移,我们不能只是使用DEL命令删除的关键lock.foo释放锁。考虑以下,进程P1首先获得锁lock.foo,然后进程P1已经挂了。过程P2,P3是经常检查锁是否已被释放或出去的时候,和执行流程如下:

P2和P3的过程,阅读的关键lock.foo值检查是否锁了时间(通过比较当前时间和关键lock.foo判断是否超时的值)
P2和P3的过程发现,锁lock.foo已经离开时间
P2执行del lock.foo命令
P2执行setnx lock.foo命令并返回1,即P2被锁
P3执行删除命令删除lock.foo P2刚建立的关键lock.foo(这步是因为P3刚刚发现锁已经离开的时间)
P3执行setnx lock.foo命令并返回1,即P3被锁
P2和P3同时锁定。


从上面可以了解到,在检测到锁超时后,进程不能简单地执行del删除键操作来获取锁。

为了解决多个进程同时发生的问题,我们将看下面的算法。



我们假定进程P1首先获得锁lock.foo,然后进程P1已经挂断。以下:

1、过程执行setnx P4 lock.foo试图获得锁



2、由于进程P1被锁定,P4执行setnx lock.foo返回0,即得到锁失效



3、P4执行得到lock.foo检测锁是否已经走出来的时候,如果不暂停,等待一段时间,再次检查



4、如果P4发现锁已经离开的时间,即当前时间大于关键lock.foo价值,P4将执行以下操作




捷先lock.foo


5、为捷先操作设置键的值,它将返回旧值的关键。通过比较是否的关键lock.foo旧值小于当前时间,可以判断过程是否已被锁定。



6,如果另一个进程P5检测到锁超时之前P4执行捷先操作,然后P4的捷先操作返回一个时间戳大于当前时间,所以P4不会得到锁,继续等待。注意即使P4然后设置的关键lock.foo价值一比P5设置较大的值,但不影响价值。



此外,值得注意的是,该过程释放锁,这是之前,执行删除lock.foo操作之前,有必要判断锁是否已经第一时间。如果锁已经超时,锁可能被其他进程获得。然后删除lock.foo操作直接执行将导致其他进程已获得的锁释放。

程序代码

以下代码是用来使用的setnx命令作为一个分布式锁的算法实现。


lock_timeout = 3
锁= 0
lock_timeout = 0
lock_key = foo的锁定。

#获取锁
而锁!= 1:
现在= int(time.time())
lock_timeout =现在+ lock_timeout + 1
锁= redis_client.setnx(lock_key,lock_timeout)
如果锁或(= =现在1 > int(redis_client.get(lock_key))和(int)现在> redis_client.getset(lock_key,lock_timeout)):
打破
其他的:
(0.001)time.sleep

#已锁定
do_job()

#释放锁
现在= int(time.time())
如果现在的<< lock_timeout:
redis_client.delete(lock_key)




以上是本文的全部内容,希望能对您有所帮助,希望大家多多支持。