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










發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章