1. 程式人生 > >Redis實現分布式鎖原理與實現分析

Redis實現分布式鎖原理與實現分析

數據表 防止 中一 csdn 訂單 not 產生 www 整體

一、關於分布式鎖

關於分布式鎖,可能絕大部分人都會或多或少涉及到。
我舉二個例子:
場景一:從前端界面發起一筆支付請求,如果前端沒有做防重處理,那麽可能在某一個時刻會有二筆一樣的單子同時到達系統後臺。

場景二:在App中下訂單的時候,點擊確認之後,沒反應,就又點擊了幾次。在這種情況下,如果無法保證該接口的冪等性,那麽將會出現重復下單問題。
在接收消息的時候,消息推送重復。如果處理消息的接口無法保證冪等,那麽重復消費消息產生的影響可能會非常大。

類似這種場景,我們有很多種方法,可以使用冪等操作,也可以使用鎖的操作。
我們先來解釋一下什麽是冪等操作:
所謂冪等,簡單地說,就是對接口的多次調用所產生的結果和調用一次是一致的。擴展一下,這裏的接口,可以理解為對外發布的HTTP接口或者Thrift接口,也可以是接收消息的內部接口,甚至是一個內部方法或操作。

在分布式環境中,網絡環境更加復雜,
因前端操作抖動、網絡故障、消息重復、響應速度慢等原因,對接口的重復調用概率會比集中式環境下更大,尤其是重復消息在分布式環境中很難避免。Tyler Treat也在《You Cannot Have Exactly-Once Delivery》一文中提到:

Within the context of a distributed system, you cannot have exactly-once message delivery.

分布式環境中,有些接口是天然保證冪等性的,如查詢操作。有些對數據的修改是一個常量,並且無其他記錄和操作,那也可以說是具有冪等性的。其他情況下,所有涉及對數據的修改、狀態的變更就都有必要防止重復性操作的發生。通過間接的實現接口的冪等性來防止重復操作所帶來的影響,成為了一種有效的解決方案。

於是我們根據以上內容就可以講一下使用分布式鎖的方法有哪些。

1、使用數據庫樂觀鎖,包括主鍵防重,版本號控制。但是這兩種方法各有利弊。

  • 使用主鍵沖突的策略進行防重,在並發量非常高的情況下對數據庫性能會有影響,尤其是應用數據表和主鍵沖突表在一個庫的時候,表現更加明顯。其實針對是否會對數據庫性能產生影響這個話題,我也和一些專業的DBA同學討論過,普遍認可的是在MySQL數據庫中采用主鍵沖突防重,在大並發情況下有可能會造成鎖表現象,比較好的辦法是在程序中生產主鍵進行防重。

  • 使用版本號策略
    這個策略源於mysql的mvcc機制,使用這個策略其實本身沒有什麽問題,唯一的問題就是對數據表侵入較大,我們要為每個表設計一個版本號字段,然後寫一條判斷sql每次進行判斷。

2、Zookeeper防重策略
利用ZK確實是一個不錯的方案,流程如下:
技術分享圖片
以前的版本中普遍傳言說它的性能不好,但是後續的版本性能得到了較大提高,經過系統壓測還是能夠支撐較大並發量的,經過壓測三臺Zookeeper能搞住20000tps。
用zookeeper的優點大概有:高可用、公平鎖、心跳保持鎖。

3、Redis防重策略
關於主從Redis方案最簡單的實現流程如下:
技術分享圖片
表面來看,這個方案似乎很管用,但是這裏存在一個問題:在我們的系統架構裏存在一個單點故障,如果Redis的master節點宕機了怎麽辦呢?有人可能會說:加一個slave節點!在master宕機時用slave就行了!但是其實這個方案明顯是不可行的,因為這種方案無法保證第1個安全互斥屬性,因為Redis的復制是異步的。 總的來說,這個方案裏有一個明顯的競爭條件(race condition),舉例來說:

  • 客戶端A在master節點拿到了鎖。
  • master節點在把A創建的key寫入slave之前宕機了。
  • slave變成了master節點
  • B也得到了和A還持有的相同的鎖(因為原來的slave裏還沒有A持有鎖的信息)

於是我就在想,我該如何做才能讓Redis在分布式鎖這一塊能夠達到高可用呢?
於是基於Tedis的思想(http://www.oschina.net/p/tedis) 我自己寫了一套針對分布式鎖的雙寫Redis框架。

二、雙寫Redis的架構圖

技術分享圖片

說明:
組件名叫YeeRedisGroup,基本服務主要有四個,當數據到來的時候,會分別插入二個Redis服務,這二個Redis服務采用的是異地雙活的方案,當其中一個Redis服務掛了以後,會將這個Redis服務從可用隊列中摘除,放入重試隊列中,另一個Redis則會繼續使用。同樣讀取Redis的時候只會從可用隊列中讀取第一個Redis服務繼續讀取。

三、雙寫Redis的類圖結構技術分享圖片

說明:這個圖其實沒什麽可說的,大家自己看就可以了。

四、雙寫Redis的時序圖

技術分享圖片
說明:這個圖主要就是說明了整體系統交互流程是怎樣的。

五、故障容錯流程圖

技術分享圖片

六、故障重試流程圖

技術分享圖片

七、主動通知與主動查詢流程圖

技術分享圖片

八、Redis可用隊列與重試隊列結構圖

技術分享圖片

Redis實現分布式鎖原理與實現分析