本文章爲你收集了分佈式的基礎理論,羅列了常見的分佈式應用場景的實現方案:分佈式鎖,分佈式事務,分佈式主鍵ID(美團的Leaf),服務限流算法,一致性hash算法。同時每一部份內容筆者都詳細的進行了收集整理並分析了每一種方案的優缺點。筆者但願該文章可以成爲網速比較全面的彙總文章,能爲讀者帶來比較系統的講解。若是讀者發現文章內還有收集不全以及錯誤的地方還請在評論區留言,筆者會盡快完善文章內容。謝謝!
CAP理論認爲:一個分佈式系統最多隻能同時知足,一致性,可用性,分區容錯性的三項中的兩項。因爲分區容錯性是必然存在的,因此大部分分佈式軟件系統都在CP和AP中作取捨redis
Base理論:即基本可用(Basically Available),軟狀態(Soft State),最終一致性(Eventually Consistent)。既然沒法作到強一致性,那麼不一樣的應用可用根據本身的業務特色,採用適當的方式來達到最終一致性。Base理論是對CAP理論的實際應用算法
一種簡單的副本控制協議,當客戶端向一個分佈式應用發送寫請求的時候,只有當全部的副本節點都更新成功以後,此次寫操做纔算成功。不然視爲失敗。這下降了寫操做的可用性,提升了讀操做的可用性。sql
假設有N個副本,客戶端向一個分佈式應用發送寫請求的時候,若是有W個副本更新成功以後,此次寫操做纔算成功。則讀操做最多須要讀N-W個副本就能讀取到最新的結果。
Quorm沒法保證強一致性,它是分佈式系統中經常使用的一種機制,用來保證數據冗餘的最終一致性的投票算法。Kafka的ISR機制有點相似該機制。數據庫
在Paxos協議中,一共有三類角色節點segmentfault
分佈式事務解決方案有 兩階段提交協議,三階段提交協議,TCC分段提交,基於消息隊列的最終一致性緩存
缺點:安全
爲解決兩階段提交協議中,公共資源佔用堵塞的問題,三階段提交協議中協調者和參與者都引入了超時機制,而後把兩階段提交協議裏的第一個階段拆分爲兩步:先詢問(CanCommit),再鎖資源(PreCommit),再最後提交(DoCommit)。服務器
PreCommit:根據CanCommit響應狀況有如下兩種執行狀況。網絡
DoCommit:階段根據PreCommit的響應也有兩種執行狀況。併發
缺點:在DoCommit階段中,假設協調者發出了事務commit的通知僅被一部分參與者所收到並執行,其他的參與者則由於沒有收到通知一直處於阻塞狀態,這時候就產生了數據的不一致性。
TCC:TCC是支付寶提出的分佈式事務解決方案,每一個分佈式事務的參與者都須要實現3個接口:try、confirm、cancel。
優勢
1. TCC解決了跨服務的業務操做原子性問題,可讓應用本身定義數據庫操做的粒度,下降鎖衝突,提升系統的業務吞吐量。 2. TCC的每一階段都由業務本身控制,避免了長事務,提升了性能。
缺點
1. 業務侵入性強:業務邏輯必須都要實現Try,Confirm,Cancel三個操做
異常狀況

解決方法:記錄事務執行狀態,若是執行過了,就再也不執行。
接口冪等性:指的是在調用方屢次調用的狀況下,接口最終獲得的數據是一致的。查詢接口具備自然的冪等性。
XA事務是基於兩階段提交協議的,XA規範主要定義了事務協調者和資源管理器之間的接口。
XA事務執行流程與兩階段提交協議差很少。
MySQL中的XA事務有兩種狀況,內部XA和外部XA。若是事務發生在MySQL服務器單機上使用內部XA,若是事務發生在多個外部節點上,使用外部XA。
內部XA: Mysql會同時維護binlog日誌與InnoDB的redolog,爲了保證兩個日誌一致性,MySql使用了XA事務。當有事務提交時:
1. 第一步:InnoDB進入Prepare階段,將事務的XID寫入redo日誌。binlog不作任何操做。 2. 第二步:進行寫binlog日誌,也將XID寫入binlog。 3. 第三部:調用InnoDB引擎的Commit完成事務的提交。而後將Commit信息寫入redo日誌。
得到鎖:當要鎖住某一個資源時,就在表中插入對應的一條記錄。
釋放鎖:刪除對應的記錄。
基於數據庫實現分佈式鎖的方案實現簡單,但有不少的問題存在。
利用Zookeeper支持的臨時順序節點的特性,能夠實現分佈式鎖。
得到鎖: 當要對某個資源加鎖時,Zookeeper在該資源對應的指定的節點目錄下,生成一個惟一的臨時節點。其餘客戶端對該節點設置一個Watcher通知。
釋放鎖:Zookeeper刪除對應的臨時節點,其餘客戶端能夠監聽到節點被刪除的通知,並從新競爭鎖。
得到讀鎖:
得到寫鎖
釋放鎖
刪除對應的臨時節點。
原理:在獲取鎖以前,先查詢一下以該鎖爲key對應的value是否存在,若存在,說明該鎖被其餘客戶端獲取了。
改進1:爲了防止獲取鎖的客戶端忽然宕機,須要在設置key的時候,指定一個過時時間,以確保即便宕機了,鎖也能最後釋放。經過SETNX命令設置key的值,經過EXPIRE命令設置過時時間。
改進2:因爲SETNX和EXPIRE命令的執行不是原子性的,多個客戶端在檢驗鎖是否存在時會致使多個客戶端都認爲本身能獲取到鎖。Redis提供了
Set原子性命令,在設置值的同時指定過時時間。
改進3:客戶端獲取鎖之後任務未執行完,但鎖已通過期,被別的客戶端獲取了,這時客戶端扔會釋放鎖,致使鎖失效。能夠在設置key的時候,設置value爲一個隨機值r,刪除的時候先比較一下redis裏的value是否爲r再決定刪除。
改進4:客戶端先比較一下redis的value是否爲r再決定刪除,但因爲比較後再刪除鎖不是原子的。在比較過程當中,key有可能因過時而被清除了致使一個客戶端就能夠獲取到key。Redis並無提供相關的比較後刪除的原子操做。此時釋放鎖的過程可使用lua腳本,redis將lua腳本的命令視爲一個原子操做。
UID使用以太網卡地址、納秒級時間、芯片ID碼和許多可能的數字來生成一串惟一隨機32位長度數據。
優勢:性能好,本地生成,全局惟一。
缺點:
對於多臺數據庫,經過每臺數據庫的起始值增值和自增值的跨度,能夠實現全局的自增ID。以4臺數據庫爲例,以下表
數據庫編號 | 起始值增值 | 自增值的跨度 | 生成的主鍵序列 |
---|---|---|---|
1 | 1 | 4 | [1,5,9,13,17,21,25,29.....] |
2 | 2 | 4 | [2,6,10,14,18,22,26,30....] |
3 | 3 | 4 | [3,7,11,15,19,23,27,31....] |
4 | 4 | 4 | [4,8,12,16,20,24,28,32....] |
優勢:容易存儲,能夠直接用數據庫存儲。
缺點:
snowflake生成id的結果是一個64bit大小的整數。由一位標識位,41個比特位的時間戳,10位的機器位,能夠標識1024臺機器,還有就是10比特位的自增序列化組成。結構以下:
優勢:趨勢遞增,不依賴第三方組件(數據庫等),性能更高,能夠根據自身業務特色動態分配bit位。
缺點:強依賴機器時鐘,若是出現時鐘回撥,那麼整個系統生成的ID將會不可用。
leaf提供了的兩種模式。
Segment模式在以前數據庫方案基礎之上進行了優化。該模式不是每次都獲取ID都操做一次數據庫,而是異步的一次性的從數據庫中取N個ID組成一個號段,而後放入本地緩存。同時採用雙buffer 方法,在第一個號段下發了必定的百分比時,就會有另外一個線程啓動來獲取並更新下一個號段的緩存數據。
優勢:
缺點:
美團Leaf的Snowflake像較於普通的Snowflake,有兩點改進。
從第一個請求進來開始計時,在接下去的1s內,每來一個請求,就把計數加1,若是累加的數字達到了100,那麼後續的請求就會被所有拒絕。等到1s結束後,把計數恢復成0,從新開始計數.可以使用redis的incr原子自增性和線程安全便可輕鬆實現。
若是我在單位時間1ms內的前10ms,已經經過了100個請求,那後面的990ms,請求所有會被拒絕,即:突刺現象。
滑動窗口算法是將時間週期分爲N個小週期,分別記錄每一個小週期內訪問次數,而且根據時間滑動刪除過時的小週期,滑動窗口的格子劃分的越多,那麼滑動窗口的滾動就越平滑,限流的統計就會越精確
算法內部有一個容器,無論上面流量多大,下面流出的速度始終保持不變。能夠準備一個隊列,用來保存請求,另外經過一個線程池按期從隊列中獲取請求並執行,能夠一次性獲取多個併發執行。
算法中存在一種機制,以必定的速率往桶中放令牌。每次請求調用須要先獲取令牌,只有拿到令牌,纔有機會繼續執行,不然選擇選擇等待可用的令牌、或者直接拒絕。能夠準備一個隊列,用來保存令牌,另外經過一個線程池按期生成令牌放到隊列中,每來一個請求,就從隊列中獲取一個令牌,並繼續執行。guava的RateLimiter能夠簡單生成一個令牌限流器。
集羣限流:每次有相關操做的時候,就向redis服務器發送一個incr命令,好比須要限制某個用戶訪問/index接口的次數,只須要拼接用戶id和接口名生成redis的key,每次該用戶訪問此接口時,只須要對這個key執行incr命令,在這個key帶上過時時間,就能夠實現指定時間的訪問頻率。
使用Hash算法讓固定的一部分請求落到同一臺服務器上,這樣每臺服務器固定處理一部分請求起到負載均衡的做用。可是普通的hash算法伸縮性不好,當新增或者下線服務器機器時候,用戶id與服務器的映射關係會大量失效。一致性hash則利用hash環對其進行了改進。
一致性hash:將全部的服務器散列值當作一個從0開始的順時針環,而後看請求的hash值落到了hash環的那個地方,在hash環上的位置順時針找距離最近的ip做爲處理服務器