分布式定時任務的redis鎖實現

來源:互聯網
上載者:User

標籤:

一個web項目如果部署為分布式時,平時常見的定時服務 在一定的間隔時間內,可能出現多次重複調用的問題。而此時由於是不同容器之間的競爭,因此需要容器層級的鎖 Redis為單進程單線程模式,採用隊列模式將並發訪問變為串列訪問。Redis本身沒有鎖的概念,Redis對於多個用戶端串連並不存在競爭。但是可以通過setnx來實現鎖

 

 


SETNX命令(SET if Not exists)

文法:
SETNX key value
功能:
將 key 的值設為 value ,若且唯若 key 不存在;若給定的 key 已經存在,則 SETNX 不做任何動作。

時間複雜度:
O(1)
傳回值:
設定成功,返回 1 。
設定失敗,返回 0 。

模式:將 SETNX 用於加鎖(locking)

SETNX 可以用作加鎖原語(locking primitive)。比如說,要對關鍵字(key) foo 加鎖,用戶端可以嘗試以下方式:

SETNX lock.foo <current Unix time + lock timeout + 1>

如果 SETNX 返回 1 ,說明用戶端已經獲得了鎖, key 設定的unix時間則指定了鎖失效的時間。之後用戶端可以通過 DEL lock.foo 來釋放鎖。

如果 SETNX 返回 0 ,說明 key 已經被其他用戶端上鎖了。如果鎖是非阻塞(non blocking lock)的,我們可以選擇返回調用,或者進入一個重試迴圈,直到成功獲得鎖或重試逾時(timeout)。

但是已經證實僅僅使用SETNX加鎖帶有競爭條件,在特定的情況下會造成錯誤。

處理死結(deadlock)

上面的鎖演算法有一個問題:如果因為用戶端失敗、崩潰或其他原因導致沒有辦法釋放鎖的話,怎麼辦?

這種狀況可以通過檢測發現——因為上鎖的 key 儲存的是 unix 時間戳記,假如 key 值的時間戳記小於當前的時間戳記,表示鎖已經不再有效。

但是,當有多個用戶端同時檢測一個鎖是否到期並嘗試釋放它的時候,我們不能簡單粗暴地刪除死結的 key ,再用 SETNX 上鎖,因為這時競爭條件(race condition)已經形成了:

C1 和 C2 讀取 lock.foo 並檢查時間戳記, SETNX 都返回 0 ,因為它已經被 C3 鎖上了,但 C3 在上鎖之後就崩潰(crashed)了。
C1 向 lock.foo 發送 DEL 命令。
C1 向 lock.foo 發送 SETNX 並成功。
C2 向 lock.foo 發送 DEL 命令。
C2 向 lock.foo 發送 SETNX 並成功。
出錯:因為競爭條件的關係,C1 和 C2 兩個都獲得了鎖。



幸好,以下演算法可以避免以上問題。來看看我們聰明的 C4 用戶端怎麼辦:

C4 向 lock.foo 發送 SETNX 命令。
因為崩潰掉的 C3 還鎖著 lock.foo ,所以 Redis 向 C4 返回 0 。
C4 向 lock.foo 發送 GET 命令,查看 lock.foo 的鎖是否到期。如果不,則休眠(sleep)一段時間,並在之後重試。
另一方面,如果 lock.foo 內的 unix 時間戳記比目前時間戳老,C4 執行以下命令:
GETSET lock.foo <current Unix timestamp + lock timeout + 1>


因為 GETSET 的作用,C4 可以檢查看 GETSET 的傳回值,確定 lock.foo 之前儲存的舊值仍是那個到期時間戳記,如果是的話,那麼 C4 獲得鎖。
如果其他用戶端,比如 C5,比 C4 更快地執行了 GETSET 操作並獲得鎖,那麼 C4 的 GETSET 操作返回的就是一個未到期的時間戳記(C5 設定的時間戳記)。C4 只好從第一步開始重試。
注意,即便 C4 的 GETSET 操作對 key 進行了修改,這對未來也沒什麼影響。

這裡假設鎖key對應的value沒有實際業務意義,否則會有問題,而且其實其value也確實不應該用在業務中。

為了讓這個加鎖演算法更健壯,獲得鎖的用戶端應該常常檢查到期時間以免鎖因諸如 DEL 等命令的執行而被意外解開,因為用戶端失敗的情況非常複雜,不僅僅是崩潰這麼簡單,還可能是用戶端因為某些操作被阻塞了相當長時間,緊接著 DEL 命令被嘗試執行(但這時鎖卻在另外的用戶端手上)。


GETSET命令
文法:GETSET key value

功能:
將給定 key 的值設為 value ,並返回 key 的舊值(old value)。當 key 存在但不是字串類型時,返回一個錯誤。

時間複雜度:
O(1)

傳回值:
返回給定 key 的舊值;當 key 沒有舊值時,也即是, key 不存在時,返回 nil 。
ref by
http://blog.csdn.net/hpb21/article/details/7893013http://redis.readthedocs.org/en/latest/string/setnx.html  可見如果要實現分布式的定時任務,只需要讓多台伺服器競爭redis的鎖即可 

分布式定時任務的redis鎖實現

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.