1,單機實現分佈式鎖的腳本(官方推薦實現)html
SET lock_key random_value NX PX 10000
// do sth
eval "if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end"
複製代碼
2,注意事項(對釋放鎖的控制,以及鎖超時的控制)random_value 要保證惟一,能夠用 trace_id 來保證!
3,存在的問題,單機Redis只是依賴單臺 Redis ,當依賴的 Redis 掛掉以後會形成比較大的問題!
4,那麼部署 Redis 的主從能夠保證嗎?主要緣由是 Redis 主節點與從節點之間的數據同步是異步的。redis
Redlock 算法是基於 N 個徹底獨立的 Redis 節點(一般狀況下 N 能夠設置成 5)。
1,獲取當前時間(毫秒數)。算法
2,按順序依次向 N 個 Redis 節點執行獲取鎖的操做。這個獲取操做跟基於單 Redis 節點的獲取鎖的過程相同。爲了保證在某個 Redis 節點不可用的時候算法可以繼續運行,這個獲取鎖的操做還有一個超時時間(time out),它要遠小於鎖的有效時間(幾十毫秒量級)。客戶端在向某個 Redis 節點獲取鎖失敗(好比該Redis節點不可用,或者該 Redis 節點上的鎖已經被其它客戶端持有)之後,應該當即嘗試下一個 Redis 節點。數據庫
3,計算整個獲取鎖的過程總共消耗了多長時間,計算方法是用當前時間減去第1步記錄的時間。若是客戶端從大多數Redis節點(>= N/2+1)成功獲取到了鎖,而且獲取鎖總共消耗的時間沒有超過鎖的有效時間(lock validity time),那麼這時客戶端才認爲最終獲取鎖成功;不然,認爲最終獲取鎖失敗。安全
4,若是最終獲取鎖成功了,那麼這個鎖的有效時間應該從新計算,它等於最初的鎖的有效時間減去第3步計算出來的獲取鎖消耗的時間。bash
5,若是最終獲取鎖失敗了(可能因爲獲取到鎖的 Redis 節點個數少於 N/2+1 ,或者整個獲取鎖的過程消耗的時間超過了鎖的最初有效時間),那麼客戶端應該當即向全部 Redis 節點發起釋放鎖的操做(這裏來保證全部的 Redis 節點均可以報以獲取的鎖釋放掉)。服務器
Redlock 算法實現的前提是基於不一樣的機器具備相同的時鐘,或者偏差很小能夠忽略不計的假設。(這也是DDIA做者噴的一點)網絡
存在的問題: 1,關於Redis的持久化的問題 假設一共有5個Redis節點:A, B, C, D, E。設想發生了以下的事件序列:
1.1 客戶端1成功鎖住了A, B, C,獲取鎖成功(但D和E沒有鎖住)。
1.2 節點C崩潰重啓了,但客戶端1在C上加的鎖沒有持久化下來,丟失了。
1.3 節點C重啓後,客戶端2鎖住了C, D, E,獲取鎖成功。
dom
Redis 給出的解決方案:延遲重啓,既當 Redis 節點掛掉以後不要馬上重啓,而要等待一個鎖的過時時間以後再重啓。異步
2,若是獲取鎖消耗的時間過多以致於沒法完成後續的操做,如何釋放鎖? 我的認爲須要業務方本身拿捏一個業務操做的須要消耗的時長,
3,Redis 做者在設計Redlock的時候,是充分考慮了網絡延遲和程序停頓所帶來的影響的。可是,對於客戶端和資源服務器之間的延遲(即發生在算法第3步以後的延遲),他認可全部的分佈式鎖的實現,包括 Redlock,是沒有什麼好辦法來應對的。
在 Martin 的這篇文章中,他把鎖的用途分爲兩種:
1,帶有自動過時功能的分佈式鎖,必須提供某種fencing機制來保證對共享資源的真正的互斥保護。Redlock 提供不了這樣一種機制。
2,Redlock 構建在一個不夠安全的系統模型之上。它對於系統的記時假設(timing assumption)有比較強的要求,而這些要求在現實的系統中是沒法保證的。 Redlock 算法對機器時鐘的強依賴,Martin 認爲算法的實現不該該對時序作任何假設:進程可能會暫停任意時長,數據包可能會在網絡中被任意延遲,時鐘可能會被任意錯誤,即使如此,該算法仍能夠正確執行並給出正確結果。
關於時鐘的不可靠性:Redis 做者認爲 Redlock 對時鐘的要求,並不須要徹底精確,它只須要時鐘差很少精確就能夠了。
3,在 Redlock 第三步完成以後的網絡延遲,也爲形成 Redlock 算法的失效,可是這個問題並非 Redlock 算法獨有的
Martin得出了以下的結論:
在瞭解了 Redlock 算法的邏輯及其可能存在的問題以後,咱們能夠針對本身的業務場景進行選擇~