redis分布式锁的实现及问题分析

Implementing a distributed lock manager with Redis.使用redis实现一个分布式锁的管理

当我们需要用不同的进程或者线程处理同一个资源的时候,锁就显得比较重要了。redis的分布式锁叫做redlock

接下来我们看一下如何用redis实现一个分布式的锁管理。

在 github上有一些使用redis分布式锁的官方的例子。 https://github.com/ronnylt/redlock-php

以php来举例:
定义三台redis服务器:
$servers = [   
['127.0.0.1'63790.01],  
['127.0.0.1'63890.01],   
['127.0.0.1'63990.01],];

创建一个锁管理:     
$redLock = new RedLock($servers);


然后锁住一个键,并且设置锁住的时间。时间是以毫秒为单位的。
$lock = $redLock->lock('my_resource_name'1000);

如果失败返回false,成功则会返回以下数据:
Array (   
[validity=> 9897.3020019531   
[resource=> my_resource_name   
[token=> 53771bfa1e775 )
我一开始看到这个的时候想到的是: 为什么要用这种形式去上锁,在我们平常的工作中,直接使用set命令
给一个key设置一个值和过期时间不久可以上锁了嘛? 我们来看看官方是如何解释的。




上锁的意义在于:这种机制在同一时间段内只允许一个请求进行操作。
举个简单的例子,比如你有3台机器要进行写操作,为了保证数据正确性,你同时只能允许一台机器进行
write操作,那么进行write操作之前必须先获取锁,获得了锁的机器可以进行操作,其他两台机器必须等待
锁的释放并获取到锁以后才能进行操作。

现在的操作系统提供了类似于flock的命令对文件进行上锁,但是如果你在多台机器上又如何操作呢?如果你需要设置处理
权限的是一个分布式的集群服务呢?

首先我们必须确立一个思想,我们必须在一个公共的地方存储一个锁。当所有的服务需要获取锁的时候去这个公共的地方获取。
这就意味着我们需要一个 “”锁系统 “”,redis是一个以内存为媒介的快速的数据库。



锁的设计:单台redis的锁实现非常简单,仅仅只是设置一个key而已,但是其中有一个问题也会导致严重的问题发
生, 我们来分析一下其中存在的陷阱:
1、客户端和redis服务端必须通过网络发生命令,这就意味着,从发送到服务端执行,其中肯定会存在一定的延迟时间。
在这段时间中redis会执行其他的命令,这就可能会让你需要获取的值发生变化。你如何保证其中只有一个程序在建立了操作这个key的连接呢?(先get再set都是一个非原子性的操作),你可以使用setnx来解决这个问题。

2、如果已经获得了锁的程序发生了崩溃,那就变成一个永远都存在的锁了,没人去取消它。这个时候我们需要设置一个过期时间来解决它。
也许我们的代码是这样:
MULTI
SETNX lock-key
PEXPIRE 10000 lock-key
EXEC

但是这又带来了另外一个问题:expire命令并不会管setnx的执行结果,不管setnx是否执行成功,expire都会执行,那是不是意味着
每个连接都会更新过期时间,key又变成了永远不会过期的键了。


上面是最 简单的办法,redis是单进程的,所以其实我们还有另外一个完美解决问题的办法,通过脚本来获取key,
你可以通过执行一个lua脚本来获取key并设置key的过期时间,redis会缓存该脚本,你可以通过eval或者evalsha来执行
脚本内容,这样就可以实现原子性的操作。并且保证key和expire都设置成功 。下面是官方提供的lua脚本:
---- Set a lock
---- KEYS[1]   - key
-- KEYS[2]   - ttl in ms
-- KEYS[3]   - lock content
local key     = KEYS[1]
local ttl     = KEYS[2]
local content = KEYS[3]
 
local lockSet = redis.call('setnx', key, content)
 
if lockSet == 1 then
  redis.call('pexpire', key, ttl)
end
 
return lockSet


这里再提供一下用node.js写的操作redis的脚本库文件,帮助我们实现分布式的锁,消息队列以及其他敏感的并发操作代码。
这个开源的nodejs脚本库叫做warlock,地址是 https://github.com/thedeveloper/warlock










你可能感兴趣的:(redis学习)