1. 程式人生 > >redis分散式鎖的實現及問題分析

redis分散式鎖的實現及問題分析

$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的指令碼庫檔案,幫助我們實現分散式的鎖,訊息佇列以及其他敏感的併發操作程式碼。