1w5 字詳細介紹分佈式系統的那些技術方案
大家好我是鹹魚了大半年的一灰灰,終於放暑假了,把小孩送回老家,作爲鹹魚的我也可以翻翻身了,接下來將趁着暑假的這段時間,將準備一個全新的分佈式專欄,爲了給大家提供更好的閱讀體驗,可以再我的個人站點上查看系列的專欄內容:
https://hhui.top / 分佈式
天天說分佈式分佈式,那麼我們是否知道什麼是分佈式,分佈式會遇到什麼問題,有哪些理論支撐,有哪些經典的應對方案,業界是如何設計並保證分佈式系統的高可用呢?
1. 架構設計
這一節將從一些經典的開源系統架構設計出發,來看一下,如何設計一個高質量的分佈式系統;
而一般的設計出發點,無外乎
• 冗餘:簡單理解爲找個備胎,現任掛掉之後,備胎頂上 • 拆分:不能讓一個人承擔所有的重任,拆分下,每個人負擔一部分,壓力均攤
1.1 主備架構
給現有的服務搭建一個備用的服務,兩者功能完全一致,區別在於平時只有主應用對外提供服務能力;而備應用則只需要保證與主應用能力一致,隨時待機即可,並不用對外提供服務;當主應用出現故障之後,將備應用切換爲主應用,原主應用下線;迅速的主備切換可以有效的縮短故障時間
基於上面的描述,主備架構特點比較清晰
• 採用冗餘的方案,加一臺備用服務 • 缺點就是資源浪費
其次就是這個架構模型最需要考慮的則是如何實現主備切換?
• 人工 •VIP(虛擬 ip) + keepalived 機制
1.2 主從架構
主從一般又叫做讀寫分離,主提供讀寫能力,而從則只提供讀能力
鑑於當下的互聯網應用,絕大多數都是讀多寫少的場景;讀更容易成爲性能瓶頸,所以採用讀寫分離,可以有效的提高整個集羣的響應能力
主從架構可以區分爲:一主多從 + 一主一從再多從,以 mysql 的主從架構模型爲例進行說明
MySql 主從
主從模式的主要特點在於
• 添加從,源頭依然是數據冗餘的思想 • 讀寫分離:主負責讀寫,從只負責讀,可以視爲負載均衡策略 • 從需要向主同步數據,所若有的從都同步與主,對主的壓力依然可能很大;所以就有了主從從的模式
關鍵問題則在於
• 主從延遲 • 主的寫瓶頸 • 主掛之後如何選主
1.3 多主多從架構
一主多從面臨單主節點的瓶頸問題,那就考慮多主多從的策略,同樣是主負責提供讀寫,從提供讀;
但是這裏有一個核心點在於多主之間的數據同步,如何保證數據的一致性是這個架構模型的重點
如 MySql 的雙主雙從可以說是一個典型的應用場景,在實際使用的時候除了上面的一致性之外,還需要考慮主鍵 id 衝突的問題
1.4 普通集羣模式
無主節點,集羣中所有的應用職能對等,沒有主次之分(當下絕大多數的業務服務都屬於這種),一個請求可以被集羣中任意一個服務響應;
這種也可以叫做去中心化的設計模式,如 redis 的集羣模式,eureka 註冊中心,以可用性爲首要目標
對於普通集羣模式而言,重點需要考慮的點在於
• 資源競爭:如何確保一個資源在同一時刻只能被一個業務操作 • 如現在同時來了申請退款和貨物出庫的請求,如果不對這個訂單進行加鎖,兩個請求同時響應,將會導致發貨又退款了,導致財貨兩失 • 數據一致性:如何確保所有的實例數據都是一致的,或者最終是一致的 • 如應用服務使用 jvm 緩存,那麼如何確保所有實例的 jvm 緩存一致? • 如 Eureka 的分區導致不同的分區的註冊信息表不一致
1.5 數據分片架構
這個分片模型的描述可能並不準確,大家看的時候重點理解一下這個思想
前面幾個的架構中,採用的是數據冗餘的方式,即所有的實例都有一個全量的數據,而這裏的數據分片,則從數據拆分的思路來處理,將全量的數據,通過一定規則拆分到多個系統中,每個系統包含部分的數據,減小單個節點的壓力,主要用於解決數據量大的場景
比如 redis 的集羣方式,通過 hash 槽的方式進行分區
如 es 的索引分片存儲
1.6 一灰灰的小結
這一節主要從架構設計層面對當前的分佈式系統所採用的方案進行了一個簡單的歸類與小結,並不一定全面,歡迎各位大佬留言指正
基於冗餘的思想:
• 主備 • 主從 • 多主多從 • 無中心集羣
基於拆分的思想:
• 數據分片
對於拆分這一塊,我們常說的分庫分表也體現的是這一思想
2. 理論基礎
這一小節將介紹分佈式系統中的經典理論,如廣爲流程的 CAP/BASE 理論,一致性理論基礎 paxios,raft,信息交換的 Gossip 協議,兩階段、三階段等
本節主要內容參考自
• 一致性算法 - Gossip 協議詳解 - 騰訊雲開發者社區 - 騰訊雲 [1] • P2P 網絡核心技術:Gossip 協議 - 知乎 [2] • 從 Paxos 到 Raft,分佈式一致性算法解析_mb5fdb0a87e2fa1 的技術博客_51CTO 博客 [3] •【理論篇】淺析分佈式中的 CAP、BASE、2PC、3PC、Paxos、Raft、ZAB - 知乎 [4]
2.1 CAP 定理
CAP 定理指出,分佈式系統 不可能 同時提供下面三個要求:
•Consistency:一致性 • 操作更新完成並返回客戶端之後,所有節點數據完全一致 •Availability:可用性 i • 服務一直可用 •Partition tolerance:分區容錯性 • 分佈式系統在遇到某節點或網絡分區故障的時候,仍然能夠對外提供滿足一致性和可用性的服務
通常來講 P 很難不保證,當服務部署到多臺實例上時,節點異常、網絡故障屬於常態,根據不同業務場景進行選擇
對於服務有限的應用而言,首選 AP,保證高可用,即使部分機器異常,也不會導致整個服務不可用;如絕大多數的前臺應用都是這種
對於數據一致性要求高的場景,如涉及到錢的支付結算,CP 可能更重要了
對於 CAP 的三種組合說明如下
2.2 BASE 理論
base 理論作爲 cap 的延伸,其核心特點在於放棄強一致性,追求最終一致性
•Basically Available: 基本可用 • 指分佈式系統在出現故障的時候,允許損失部分可用性,即保證核心可用 • 如大促時降級策略 •Soft State:軟狀態 • 允許系統存在中間狀態,而該中間狀態不會影響系統整體可用性 •MySql 異步方式的主從同步,可能導致的主從數據不一致 •Eventual Consistency:最終一致性 • 最終一致性是指系統中的所有數據副本經過一定時間後,最終能夠達到一致的狀態
基於上面的描述,可以看到 BASE 理論適用於大型高可用可擴展的分佈式系統
注意其不同於 ACID 的強一致性模型,而是通過犧牲強一致性 來獲得可用性,並允許數據在一段時間內是不一致的,但最終達到一致狀態
2.3 PACELEC 定理
這個真沒聽說過,以下內容來自:
•Distributed System Design Patterns | by Nishant | Medium[5]
• 如果有一個分區('P'),分佈式系統可以在可用性和一致性(即'A'和'C')之間進行權衡;• 否則('E'),當系統在沒有分區的情況下正常運行時,系統可以在延遲('L')和一致性('C')之間進行權衡。
定理(PAC)的第一部分與 CAP 定理相同,ELC 是擴展。整個論點假設我們通過複製來保持高可用性。因此,當失敗時,CAP 定理佔上風。但如果沒有,我們仍然必須考慮複製系統的一致性和延遲之間的權衡。
2.4 Paxos 共識算法
Paxos 算法解決的問題是分佈式共識性問題,即一個分佈式系統中的各個進程如何就某個值(決議)通過共識達成一致
基於上面這個描述,可以看出它非常適用於選舉;其工作流程
• 一個或多個提議進程 (Proposer) 可以發起提案 (Proposal), • Paxos 算法使所有提案中的某一個提案,在所有進程中達成一致。系統中的多數派同時認可該提案,即達成了一致
角色劃分:
• Proposer: 提出提案 Proposal,包含編號 + value•Acceptor: 參與決策,迴應 Proposers 的提案;當一個提案,被半數以上的 Acceptor 接受,則該提案被批准 • 每個 acceptor 只能批准一個提案 • Learner: 不參與決策,獲取最新的提案 value
2.5 Raft 算法
推薦有興趣的小夥伴,查看
•Raft 算法動畫演示 [6] •Raft 算法詳解 - 知乎 [7]
爲了解決 paxos 的複雜性,raft 算法提供了一套更易理解的算法基礎,其核心流程在於:
leader 接受請求,並轉發給 follow,當大部分 follow 響應之後,leader 通知所有的 follow 提交請求、同時自己也提交請求並告訴調用方 ok
角色劃分:
•Leader:領導者,接受客戶端請求,並向 Follower 同步請求,當數據同步到大多數節點上後告訴 Follower 提交日誌 •Follow: 接受並持久化 Leader 同步的數據,在 Leader 告之日誌可以提交之後,提交 •Candidate:Leader 選舉過程中的臨時角色,向其他節點拉選票,得到多數的晉升爲 leader,選舉完成之後不存在這個角色
raft 共識流程
2.6 ZAB 協議
ZAB(Zookeeper Atomic Broadcast) 協議是爲分佈式協調服務 ZooKeeper 專門設計的一種支持崩潰恢復的一致性協議,基於該協議,ZooKeeper 實現了一種 主從模式的系統架構來保持集羣中各個副本之間的數據一致性。
•zookeeper 核心之 ZAB 協議就這麼簡單![8]
主要用於 zk 的數據一致性場景,其核心思想是 Leader 再接受到事務請求之後,通過給 Follower,當半數以上的 Follower 返回 ACK 之後,Leader 提交提案,並向 Follower 發送 commit 信息
角色劃分
•Leader: 負責整個 Zookeeper 集羣工作機制中的核心 • 事務請求的唯一調度和處理者,保證集羣事務處理的順序性 • 集羣內部各服務器的調度者
•Follower:Leader 的追隨者 • 處理客戶端的非實物請求,轉發事務請求給 Leader 服務器 • 參與事務請求 Proposal 的投票 • 參與 Leader 選舉投票
•Observer:是 zookeeper 自 3.3.0 開始引入的一個角色, • 它不參與事務請求 Proposal 的投票, • 也不參與 Leader 選舉投票 • 只提供非事務的服務(查詢),通常在不影響集羣事務處理能力的前提下提升集羣的非事務處理能力。
ZAB 消息廣播
2.7 2PC 協議
two-phase commit protocol,兩階段提交協議,主要是爲了解決強一致性,中心化的強一致性協議
角色劃分
• 協調節點 (coordinator):中心化
• 參與者節點 (partcipant):多個
執行流程
協調節點接收請求,然後向參與者節點提交 precommit
,當所有的參與者都回復 ok 之後,協調節點再給所有的參與者節點提交commit
,所有的都返回 ok 之後,才表明這個數據確認提交
當第一個階段,有一個參與者失敗,則所有的參與者節點都回滾
2pc 流程
特點
優點在於實現簡單
缺點也很明顯
• 協調節點的單點故障 • 第一階段全部 ack 正常,第二階段存在部分參與者節點異常時,可能出現不一致問題
2.8 3PC 協議
分佈式事務:兩階段提交與三階段提交 - SegmentFault 思否 [9]
在兩階段的基礎上進行擴展,將第一階段劃分兩部,cancommit + precommit,第三階段則爲 docommit
第一階段 cancommit
該階段協調者會去詢問各個參與者是否能夠正常執行事務,參與者根據自身情況回覆一個預估值,相對於真正的執行事務,這個過程是輕量的
第二階段 precommit
本階段協調者會根據第一階段的詢盤結果採取相應操作,若所有參與者都返回 ok,則協調者向參與者提交事務執行 (單不提交) 通知;否則通知參與者 abort 回滾
第三階段 docommit
如果第二階段事務未中斷,那麼本階段協調者將會依據事務執行返回的結果來決定提交或回滾事務,若所有參與者正常執行,則提交;否則協調者 + 參與者回滾
在本階段如果因爲協調者或網絡問題,導致參與者遲遲不能收到來自協調者的 commit 或 rollback 請求,那麼參與者將不會如兩階段提交中那樣陷入阻塞,而是等待超時後繼續 commit,相對於兩階段提交雖然降低了同步阻塞,但仍然無法完全避免數據的不一致
特點
• 降低了阻塞與單點故障: • 參與者返回 CanCommit 請求的響應後,等待第二階段指令,若等待超時 / 協調者宕機,則自動 abort,降低了阻塞; • 參與者返回 PreCommit 請求的響應後,等待第三階段指令,若等待超時 / 協調者宕機,則自動 commit 事務,也降低了阻塞;
• 數據不一致問題依然存在 • 比如第三階段協調者發出了 abort 請求,然後有些參與者沒有收到 abort,那麼就會自動 commit,造成數據不一致
2.9 Gossip 協議
Gossip 協議,顧名思義,就像流言蜚語一樣,利用一種隨機、帶有傳染性的方式,將信息傳播到整個網絡中,並在一定時間內,使得系統內的所有節點數據一致。Gossip 協議通過上面的特性,可以保證系統能在極端情況下(比如集羣中只有一個節點在運行)也能運行
•P2P 網絡核心技術:Gossip 協議 - 知乎 [10]
主要用在分佈式數據庫系統中各個副本節點同步數據之用,這種場景的一個最大特點就是組成的網絡的節點都是對等節點,是非結構化網絡
工作流程
• 週期性的傳播消息,通常週期時間爲 1s • 被感染的節點,隨機選擇 n 個相鄰節點,傳播消息 • 每次傳播消息都選擇還沒有發送過的節點進行傳播 • 收單消息的節點,不會傳播給向它發送消息的節點
Gossip 傳播示意圖
特點
• 擴展性:允許節點動態增加、減少,新增的節點狀態最終會與其他節點一致 • 容錯:網絡中任意一個節點宕機重啓都不會影響消息傳播 • 去中心化:不要求中心節點,所有節點對等,任何一個節點無需知道整個網絡狀況,只要網絡連通,則一個節點的消息最終會散播到整個網絡 • 一致性收斂:協議中的消息會以一傳十、十傳百一樣的指數級速度在網絡中快速傳播,因此係統狀態的不一致可以在很快的時間內收斂到一致。消息傳播速度達到了 logN• 簡單:Gossip 協議的過程極其簡單,實現起來幾乎沒有太多複雜性
缺點
• 消息延遲:節點只會隨機向少數幾個節點發送消息,消息最終是通過多個輪次的散播而到達全網的,因此使用 Gossip 協議會造成不可避免的消息延遲 • 消息冗餘:節點會定期隨機選擇周圍節點發送消息,而收到消息的節點也會重複該步驟,導致消息的冗餘
2.10 一灰灰的小結
本節主要介紹的是分佈式系統設計中的一些常見的理論基石,如分佈式中如何保障一致性,如何對一個提案達成共識
•BASE,CAP,PACELEC 理論:構建穩定的分佈式系統應該考慮的方向 •paxos,raft 共識算法 •zab 一致性協議 •gossip 消息同步協議
3. 算法
這一節將主要介紹下分佈式系統中的經典的算法,比如常用於分區的一致性 hash 算法,適用於一致性的 Quorum NWR 算法,PBFT 拜占庭容錯算法,區塊鏈中大量使用的工作量證明 PoW 算法等
3.1 一致性 hash 算法
一致性 hash 算法,主要應用於數據分片場景下,有效降低服務的新增、刪除對數據複製的影響
通過對數據項的鍵進行哈希處理映射其在環上的位置,然後順時針遍歷環以查找位置大於該項位置的第一個節點,將每個由鍵標識的數據分配給 hash 環中的一個節點
一致性 hash 算法
一致散列的主要優點是增量穩定性; 節點添加刪除,對整個集羣而言,僅影響其直接鄰居,其他節點不受影響。
注意:
•redis 集羣實現了一套 hash 槽機制,其核心思想與一致性 hash 比較相似
3.2 Quorum NWR 算法
用來保證數據冗餘和最終一致性的投票算法,其主要數學思想來源於鴿巢原理
• 分佈式系統之 Quorum (NRW)算法 - 阿里雲開發者社區 [11]
•N 表示副本數,又叫做複製因子(Replication Factor)。也就是說,N 表示集羣中同一份數據有多少個副本 •W,又稱寫一致性級別(Write Consistency Level),表示成功完成 W 個副本更新寫入,纔會視爲本次寫操作成功 •R 又稱讀一致性級別(Read Consistency Level),表示讀取一個數據對象時需要讀 R 個副本, 纔會視爲本次讀操作成功
Quorum NWR 算法要求每個數據拷貝對象 都可以投 1 票,而每一個操作的執行則需要獲取最小的讀票數,寫票數;通常來講寫票數 W 一般需要超過 N/2,即我們通常說的得到半數以上的票才表示數據寫入成功
事實上當 W=N、R=1 時,即所謂的 WARO(Write All Read One)。就是 CAP 理論中 CP 模型的場景
3.3 PBFT 拜占庭算法
拜占庭算法主要針對的是分佈式場景下無響應,或者響應不可信的情況下的容錯問題,其核心分三段流程,如下
拜占庭算法
假設集羣節點數爲 N,f 個故障節點 (無響應) 和 f 個問題節點(無響應或錯誤響應),f+1 個正常節點,即 3f+1=n
• 客戶端向主節點發起請求,主節點接受請求之後,向其他節點廣播 pre-prepare 消息 • 節點接受 pre-prepare 消息之後,若同意請求,則向其他節點廣播 prepare 消息; • 當一個節點接受到 2f+1 個 prepare 新消息,則進入 commit 階段,並廣播 commit 消息 • 當收到 2f+1 個 commit 消息後(包括自己),代表大多數節點已經進入 commit 階段,這一階段已經達成共識,於是節點就會執行請求,寫入數據
相比 Raft 算法完全不適應有人作惡的場景,PBFT 算法能容忍 (n 1)/3 個惡意節點 (也可以是故障節點)。另外,相比 PoW 算法,PBFT 的優點是不消耗算 力。PBFT 算法是 O(n ^ 2) 的消息複雜度的算法,所以以及隨着消息數 的增加,網絡時延對系統運行的影響也會越大,這些都限制了運行 PBFT 算法的分佈式系統 的規模,也決定了 PBFT 算法適用於中小型分佈式系統
3.4 PoW 算法
工作量證明 (Proof Of Work,簡稱 PoW),同樣應用於分佈式下的一致性場景,區別於前面的 raft, pbft, paxos 採用投票機制達成共識方案,pow 採用工作量證明
客戶端需要做一定難度的工作才能得出一個結果,驗證方卻很容易通過結果來檢查出客戶端是不是做了相應的工作,通過消耗一定工作浪,增加消息僞造的成本,PoW 以區塊鏈中廣泛應用而廣爲人知,下面以區塊鏈來簡單說一下 PoW 的算法應用場景
以 BTC 的轉賬爲例,A 轉 n 個 btc 給 B,如何保證不會同時將這 n 個幣轉給 C?
•A 轉賬給 B,交易信息記錄在一個區塊 1 中 •A 轉賬給 C,交易信息被記錄在另一個區塊 2 中 • 當區塊 1 被礦工成功提交到鏈上,並被大多數認可(通過校驗區塊鏈上的 hash 值驗證是否準確,而這個 hash 值體現的是礦工的工作量),此時尚未提交的區塊 2 則會被拋棄 • 若區塊 1 被提交,區塊 2 也被提交,各自有部分人認可,就會導致分叉,區塊鏈中採用的是優選最長的鏈作爲主鏈,丟棄分叉的部分(這就屬於區塊鏈的知識點了,有興趣的小夥伴可以擴展下相關知識點,這裏就不展開了)
PoW 的算法,主要應用在上面的區塊提交驗證,通過 hash 值計算來消耗算力,以此證明礦工確實有付出,得到多數認可的可以達成共識
3.5 一灰灰的小結
本節主要介紹了下當前分佈式下常見的算法,
• 分區的一致性 hash 算法: 基於 hash 環,減少節點動態增加減少對整個集羣的影響;適用於數據分片的場景 • 適用於一致性的 Quorum NWR 算法: 投票算法,定義如何就一個提案達成共識 •PBFT 拜占庭容錯算法: 適用於集羣中節點故障、或者不可信的場景 • 區塊鏈中大量使用的工作量證明 PoW 算法: 通過工作量證明,認可節點的提交
4. 技術思想
這一節的內容相對前面幾個而言,並不太容易進行清晰的分類;主要包含一些高質量的分佈式系統的實踐中,值得推薦的設計思想、技術細節
4.1 CQRS
•DDD 中的那些模式 — CQRS - 知乎 [12]• 詳解 CQRS 架構模式_架構_Kislay Verma_InfoQ 精選文章 [13]
Command Query Responsibility Segregation 即我們通俗理解的讀寫分離,其核心思想在於將兩類不同操作進行分離,在獨立的服務中實現
cqrs
用途在於將領域模型與查詢功能進行分離,讓一些複雜的查詢擺脫領域模型的限制,以更爲簡單的 DTO 形式展現查詢結果。同時分離了不同的數據存儲結構,讓開發者按照查詢的功能與要求更加自由的選擇數據存儲引擎
4.2 複製負載平衡服務
• 分佈式系統設計: 服務模式之複製負載平衡服務 - 知乎 [14]• 負載均衡調度算法大全 | 菜鳥教程 [15]
複製負載平衡服務 (Replication Load Balancing Service, RLBS),可以簡單理解爲我們常說的負載均衡,多個相同的服務實例構建一個集羣,每個服務都可以響應請求,負載均衡器負責請求的分發到不同的實例上,常見的負載算法
4.3 心跳機制
在分佈式環境裏中,如何判斷一個服務是否存活,當下最常見的方案就是心跳
比如 raft 算法中的 leader 向所有的 follow 發送心跳,表示自己還健在,避免發生新的選舉;
比如 redis 的哨兵機制,也是通過 ping/pong 的心跳來判斷節點是否下線,是否需要選新的主節點;
再比如我們日常的業務應用得健康監測,判斷服務是否正常
4.4 租約機制
租約就像一個鎖,但即使客戶端離開,它也能工作。客戶端請求有限期限的租約,之後租約到期。如果客戶端想要延長租約,它可以在租約到期之前續訂租約。
租約主要是了避免一個資源長久被某個對象持有,一旦對方掛了且不會主動釋放的問題;在實際的場景中,有兩個典型的應用
case1 分佈式鎖
業務獲取的分佈式鎖一般都有一個有效期,若有效期內沒有主動釋放,這個鎖依然會被釋放掉,其他業務也可以搶佔到這把鎖;因此對於持有鎖的業務方而言,若發現在到期前,業務邏輯還沒有處理完,則可以續約,讓自己繼續持有這把鎖
典型的實現方式是 redisson 的看門狗機制
case2 raft 算法的任期
在 raft 算法中,每個 leader 都有一個任期,任期過後會重新選舉,而 Leader 爲了避免重新選舉,一般會定時發送心跳到 Follower 進行續約
4.5 Leader & Follow
這個比較好理解,上面很多系統都採用了這種方案,特別是在共識算法中,由領導者負責代表整個集羣做出決策,並將決策傳播到所有其他服務器
領導者選舉在服務器啓動時進行。每個服務器在啓動時都會啓動領導者選舉,並嘗試選舉領導者。除非選出領導者,否則系統不接受任何客戶端請求
4.6 Fencing
在領導者 - 追隨者模式中,當領導者失敗時,不可能確定領導者已停止工作,如慢速網絡或網絡分區可能會觸發新的領導者選舉,即使前一個領導者仍在運行並認爲它仍然是活動的領導者
Fencint 是指在以前處於活動狀態的領導者周圍設置圍欄,使其無法訪問集羣資源,從而停止爲任何讀 / 寫請求提供服務
• 資源屏蔽:系統會阻止以前處於活動狀態的領導者訪問執行基本任務所需的資源。• 節點屏蔽:系統會阻止以前處於活動狀態的領導者訪問所有資源。執行此操作的常見方法是關閉節點電源或重置節點。
4.7 Quorum 法定人數
法定人數,常見於選舉、共識算法中,當超過 Quorum 的節點數確認之後,才表示這個提案通過 (數據更新成功),通常這個法定人數爲 = 半數節點 + 1
4.8 High-Water mark 高水位線
高水位線,跟蹤 Leader(領導者)上的最後一個日誌條目,且該條目已成功複製到 > quorum(法定人數)的 Follow(跟誰者),即表示這個日誌被整個集羣接受
日誌中此條目的索引稱爲高水位線索引。領導者僅公開到高水位線索引的數據。
如 Kafka:爲了處理非可重複讀取並確保數據一致性,Kafka broker 會跟蹤高水位線,這是特定分區的最大偏移量。使用者只能看到高水位線之前的消息。
4.9 Phi 累計故障檢測
Phi Accrual Failure Detection, 使用歷史檢測信號信息使閾值自適應
通用的應計故障檢測器不會判斷服務器是否處於活動狀態,而是輸出有關服務器的可疑級別。
如 Cassandra(Facebook 開源的分佈式 NoSql 數據庫)使用 Phi 應計故障檢測器算法來確定羣集中節點的狀態
4.10 Write-ahead Log 預寫日誌
預寫日誌記錄是解決操作系統中文件系統不一致的問題的高級解決方案,當我們提交寫到操作系統的文件緩存,此時業務會認爲已經提交成功;但是在文件緩存與實際寫盤之間會有一個時間差,若此時機器宕機,會導致緩存中的數據丟失,從而導致完整性缺失
爲了解決這個問題,如 mysql,es 等都採用了預寫日誌的機制來避免這個問題
MySql:
• 事務提交的流程中,先寫 redolog precommit, 然後寫 binlog,最後再 redolog commit;當 redolog 記錄成功之後,才表示事務執行成功; • 因此當出現上面的宕機恢復時,則會加載 redologo,然後重放對應的命令,來恢復未持久化的數據
ElasticSearch:
• 在內存中數據生成段寫到操作系統文件緩存前,會先寫事務日誌,出現異常時,也是從事務日誌進行恢復
4.11 分段日誌
將日誌拆分爲多個較小的文件,而不是單個大文件,以便於操作。
單個日誌文件在啓動時讀取時可能會增長併成爲性能瓶頸。較舊的日誌會定期清理,並且很難對單個大文件執行清理操作。
單個日誌拆分爲多個段。日誌文件在指定的大小限制後滾動。使用日誌分段,需要有一種將邏輯日誌偏移量(或日誌序列號)映射到日誌段文件的簡單方法。
這個其實也非常常見,比如我們實際業務應用配置的 log,一般都是按天、固定大小進行拆分,並不會把所有的日誌都放在一個日誌文件中
再比如 es 的分段存儲,一個段就是一個小的存儲文件
4.12 checksum 校驗
在分佈式系統中,在組件之間移動數據時,從節點獲取的數據可能會損壞。
計算校驗和並將其與數據一起存儲。
要計算校驗和,請使用 MD5、SHA-1、SHA-256 或 SHA-512 等加密哈希函數。哈希函數獲取輸入數據並生成固定長度的字符串(包含字母和數字); 此字符串稱爲校驗和。
當系統存儲某些數據時,它會計算數據的校驗和,並將校驗和與數據一起存儲。當客戶端檢索數據時,它會驗證從服務器接收的數據是否與存儲的校驗和匹配。如果沒有,則客戶端可以選擇從另一個副本檢索該數據。
HDFS 和 Chubby 將每個文件的校驗和與數據一起存儲。
4.13 一灰灰的小結
這一節很多內容來自下面這篇博文,推薦有興趣的小夥伴查看原文
•Distributed System Design Patterns | by Nishant | Medium[16]
這一節主要簡單的介紹了下分佈式系統中應用到的一些技術方案,如有對其中某個技術有興趣的小夥伴可以留言,後續會逐一進行補全
5. 分佈式系統解決方案
最後再介紹一些常見的分佈式業務場景及對應的解決方案,比如全局唯一的遞增 ID - 雪花算法,分佈式系統的資源搶佔 - 分佈式鎖,分佈式事務 - 2pc/3pc/tcc ,分佈式緩存等
5.1 緩存
緩存實際上並不是分佈式獨有的,這裏把它加進來,主要是因爲實在是應用得太廣了,無論是應用服務、基礎軟件工具還是操作系統,大量都可以見到緩存的身影
緩存的核心思想在於:藉助更高效的 IO 方式,來替代代價昂貴的 IO 方式
如:
•redis 的性能高於 mysql • 如內存的讀寫,遠高於磁盤 IO,文件 IO • 磁盤順序讀寫 > 隨機讀寫
用好緩存可以有效提高應用性能,下面以一個普通的 java 前臺應用爲例說明
•JVM 緩存 -> 分佈式緩存 (redis/memcache) -> mysql 緩存 -> 操作系統文件緩存 -> 磁盤文件
緩存面臨的核心問題,則在於
• 一致性問題:緩存與 db 的一致性如何保障(相信大家都聽說過或者實際處理過這種問題)• 數據完整性:比如常見的先寫緩存,異步刷新到磁盤,那麼緩存到磁盤刷新這段時間內,若宕機導致數據丟失怎麼辦?•TIP: 上面這個問題可以參考 mysql 的 redolog
5.2 全局唯一 ID
在傳統的單體架構中,業務 id 基本上是依賴於數據庫的自增 id 來處理;當我們進入分佈式場景時,如我們常說的分庫分表時,就需要我們來考慮如何實現全局唯一的業務 id 了,避免出現在分表中出現衝突
全局唯一 ID 解決方案:
•uuid • 數據庫自增 id 表 •redis 原子自增命令 • 雪花算法 (原生的,擴展的百度 UidGenerator, 美團 Leaf 等) •Mist 薄霧算法
5.3 分佈式鎖
常用於分佈式系統中資源控制,只有持有鎖的才能繼續操作,確保同一時刻只會有一個實例訪問這個資源
常見的分佈式鎖有
• 基於數據庫實現分佈式鎖 •Redis 實現分佈式鎖(應用篇) | 一灰灰 Learning[17] • 從 0 到 1 實現一個分佈式鎖 | 一灰灰 Learning[18] •etcd 實現分佈式鎖 • 基於 consul 實現分佈式鎖
5.4 分佈式事務
事務表示一組操作,要麼全部成功,要麼全部不成功;單機事務通常說的是數據庫的事務;而分佈式事務,則可以簡單理解爲多個數據庫的操作,要麼同時成功,要麼全部不成功
更確切一點的說法,分佈式事務主要是要求事務的參與方,可能涉及到多個系統、多個數據資源,要求它們的操作要麼都成功,要麼都回滾;
一個簡單的例子描述下分佈式事務場景:
下單扣庫存
• 用戶下單,付錢 • 此時訂單服務,會生成訂單信息 • 支付網關,會記錄付款信息,成功 or 失敗 • 庫存服務,扣減對應的庫存
一個下單支付操作,涉及到三個系統,而分佈式事務則是要求,若支付成功,則上面三個系統都應該更新成功;若有一個操作失敗,如支付失敗,則已經扣了庫存的要回滾(還庫存),生成的訂單信息回滾(刪掉 -- 注:現實中並不會去刪除訂單信息,這裏只是用於說明分佈式事務,請勿帶入實際的實現方案)
分佈式事務實現方案:
•2PC: 前面說的兩階段提交,就是實現分佈式事務的一個經典解決方案 •3PC: 三階段提交 •TCC:補償事務,簡單理解爲應用層面的 2PC •SAGA 事務 • 本地消息表 •MQ 事務方案
5.5 分佈式任務
分佈式任務相比於我們常說單機的定時任務而言,可以簡單的理解爲多臺實例上的定時任務,從應用場景來說,可以區分兩種
• 互斥性的分佈式任務 • 即同一時刻,集羣內只能有一個實例執行這個任務 • 並存式的分佈式任務 • 同一時刻,所有的實例都可以執行這個任務 • 續考慮如何避免多個任務操作相同的資源
分佈式任務實現方案:
•Quartz Cluster •XXL-Job•Elastic-Job • 自研: • 資源分片策略 • 分佈式鎖控制的唯一任務執行策略
5.6 分佈式 Session
Session 一般叫做會話,Session 技術是 http 狀態保持在服務端的解決方案,它是通過服務器來保持狀態的。我們可以把客戶端瀏覽器與服務器之間一系列交互的動作稱爲一個 Session。是服務器端爲客戶端所開闢的存儲空間,在其中保存的信息就是用於保持狀態。因此,session 是解決 http 協議無狀態問題的服務端解決方案,它能讓客戶端和服務端一系列交互動作變成一個完整的事務。
單機基於 session/cookie 來實現用戶認證,那麼在分佈式系統的多實例之間,如何驗證用戶身份呢?這個就是我們說的分佈式 session
分佈式 session 實現方案:
•session stick:客戶端每次請求都轉發到同一臺服務器 (如基於 ip 的 hash 路由轉發策略) •session 複製: session 生成之後,主動同步給其他服務器 •session 集中保存:用戶信息統一存儲,每次需要時統一從這裏取 (也就是常說的 redis 實現分佈式 session 方案) •cookie: 使用客戶端 cookie 存儲 session 數據,每次請求時攜帶這個
5.7 分佈式鏈路追蹤
分佈式鏈路追蹤也可以叫做全鏈路追中,而它可以說是每個開發者的福音,通常指的是一次前端的請求,將這個請求過程中,所有涉及到的系統、鏈路都串聯起來,可以清晰的知道這一次請求中,調用了哪些服務,有哪些 IO 交互,瓶頸點在哪裏,什麼地方拋出了異常
當前主流的全鏈路方案大多是基於 google 的Dapper
論文實現的
全鏈路實現方案
•zipkin •pinpoint •SkyWalking •CAT •jaeger
5.8 布隆過濾器
Bloom 過濾器是一種節省空間的概率數據結構,用於測試元素是否爲某集合的成員。
布隆過濾器由一個長度爲 m 比特的位數組(bit array)與 k 個哈希函數(hash function)組成的數據結構。
原理是當一個元素被加入集合時,通過 K 個散列函數將這個元素映射成一個位數組中的 K 個點,把它們置爲 1。
檢索時,我們只要看看這些點是不是都是 1 就大約知道集合中有沒有它了,也就是說,如果這些點有任何一個 0 ,則被檢元素一定不在;如果都是 1 ,則被檢元素很可能在。
關於布隆過濾器,請牢記一點
• 判定命中的,不一定真的命中 • 判定沒有命中的,則一定不在裏面
布隆過濾器
常見的應用場景,如
• 防止緩存穿透 • 爬蟲時重複檢測
5.9 一灰灰的小結
分佈式系統的解決方案當然不侷限於上面幾種,比如分佈式存儲、分佈式計算等也屬於常見的場景,當然在我們實際的業務支持過程中,不太可能需要讓我們自己來支撐這種大活;而上面提到的幾個點,基本上或多或少會與我們日常工作相關,這裏列出來當然是好爲了後續的詳情做鋪墊
6. 一灰灰的總結
6.1 綜述
這是一篇概括性的綜述類文章,可能並沒有很多的乾貨,當然也限於 “一灰灰” 我個人的能力,上面的總結可能並不準確,如有發現,請不吝賜教
全文總結如下
常見的分佈式架構設計方案:
• 主備,主從,多主多從,普通無中心集羣,數據分片架構
分佈式系統中的理論基石:
•CAP, BASE, PACELEC • 共識算法:paxos, raft, zab • 一致性協議:2pc, 3pc • 數據同步:gossip
分佈式系統中的算法:
• 分區的一致性 hash 算法: 基於 hash 環,減少節點動態增加減少對整個集羣的影響;適用於數據分片的場景 • 適用於一致性的 Quorum NWR 算法: 投票算法,定義如何就一個提案達成共識 • PBFT 拜占庭容錯算法: 適用於集羣中節點故障、或者不可信的場景 • 區塊鏈中大量使用的工作量證明 PoW 算法: 通過工作量證明,認可節點的提交
分佈式系統解決方案:
• 分佈式緩存 • 全局唯一 ID • 分佈式鎖 • 分佈式事務 • 分佈式任務 • 分佈式會話 • 分佈式鏈路追蹤 • 布隆過濾器
6.2 題外話
最後總結一下這篇耗時兩週寫完的 “心血鉅作”(有點自吹了哈),準備這篇文章確實花了很大的精力,首先我個人對於分佈式這塊的理解並不能算深刻,其次分佈式這塊的理論 + 實踐知識特別多,而且並不是特別容易上手理解,在輸出這篇文章的同時,遇到一些疑問點我也會去查閱相關資料去確認,整個過程並不算特別順利;那麼爲什麼還要去做這個事情呢?
- 鹹魚太久了,想做一些有意思的東西,活躍一下大腦
- 準備依託於《分佈式專欄》來將自己的知識體系進行歸納彙總,讓零散分佈在大腦中的知識點能有一個脈絡串聯起來
- 不想做架構的碼農不是好碼農,而想成爲一個好的架構,當然得做一些基礎準備,向業務精品學習取經
References
[1]
一致性算法 - Gossip 協議詳解 - 騰訊雲開發者社區 - 騰訊雲: https://cloud.tencent.com/developer/article/1662426
[2]
P2P 網絡核心技術:Gossip 協議 - 知乎: https://zhuanlan.zhihu.com/p/41228196
[3]
從 Paxos 到 Raft,分佈式一致性算法解析_mb5fdb0a87e2fa1 的技術博客_51CTO 博客: https://blog.51cto.com/u_15060467/2678779
[4]
【理論篇】淺析分佈式中的 CAP、BASE、2PC、3PC、Paxos、Raft、ZAB - 知乎: https://zhuanlan.zhihu.com/p/338628717
[5]
Distributed System Design Patterns | by Nishant | Medium: https://medium.com/@nishantparmar/distributed-system-design-patterns-2d20908fecfc
[6]
Raft 算法動畫演示: http://thesecretlivesofdata.com/raft/
[7]
Raft 算法詳解 - 知乎: https://zhuanlan.zhihu.com/p/32052223
[8]
zookeeper 核心之 ZAB 協議就這麼簡單!: https://segmentfault.com/a/1190000037550497
[9]
分佈式事務:兩階段提交與三階段提交 - SegmentFault 思否: https://segmentfault.com/a/1190000012534071
[10]
P2P 網絡核心技術:Gossip 協議 - 知乎: https://zhuanlan.zhihu.com/p/41228196
[11]
分佈式系統之 Quorum (NRW)算法 - 阿里雲開發者社區: https://developer.aliyun.com/article/53498
[12]
DDD 中的那些模式 — CQRS - 知乎: https://zhuanlan.zhihu.com/p/115685384
[13]
詳解 CQRS 架構模式_架構_Kislay Verma_InfoQ 精選文章: https://www.infoq.cn/article/wdlpjosudoga34jutys9
[14]
分佈式系統設計: 服務模式之複製負載平衡服務 - 知乎: https://zhuanlan.zhihu.com/p/34191846
[15]
負載均衡調度算法大全 | 菜鳥教程: https://www.runoob.com/w3cnote/balanced-algorithm.html
[16]
Distributed System Design Patterns | by Nishant | Medium: https://medium.com/@nishantparmar/distributed-system-design-patterns-2d20908fecfc
[17]
Redis 實現分佈式鎖(應用篇) | 一灰灰 Learning: https://hhui.top/spring-db/09.%E5%AE%9E%E4%BE%8B/20.201030-springboot%E7%B3%BB%E5%88%97%E6%95%99%E7%A8%8Bredis%E5%AE%9E%E7%8E%B0%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E5%BA%94%E7%94%A8%E7%AF%87/
[18]
從 0 到 1 實現一個分佈式鎖 | 一灰灰 Learning: https://hhui.top/spring-middle/03.zookeeper/02.210415-springboot%E6%95%B4%E5%90%88zookeeper%E4%BB%8E0%E5%88%B01%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AA%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/aFnQp1MKYQOXjqBxHZpJ8w