
大家好我是鹹魚了大半年的一灰灰,終于放暑假了,把小孩送回老家,作為鹹魚的我也可以翻翻身了,接下來将趁着暑假的這段時間,将準備一個全新的分布式專欄,為了給大家提供更好的閱讀體驗,可以再我的個人站點上檢視系列的專欄内容:
https://hhui.top/分布式
天天說分布式分布式,那麼我們是否知道什麼是分布式,分布式會遇到什麼問題,有哪些理論支撐,有哪些經典的應對方案,業界是如何設計并保證分布式系統的高可用呢?
1.架構設計
這一節将從一些經典的開源系統架構設計出發,來看一下,如何設計一個高品質的分布式系統;
而一般的設計出發點,無外乎
- 備援:簡單了解為找個備胎,現任挂掉之後,備胎頂上
- 拆分:不能讓一個人承擔所有的重任,拆分下,每個人負擔一部分,壓力均攤
1.1 主備架構
給現有的服務搭建一個備用的服務,兩者功能完全一緻,差別在于平時隻有主應用對外提供服務能力;而備應用則隻需要保證與主應用能力一緻,随時待機即可,并不用對外提供服務;當主應用出現故障之後,将備應用切換為主應用,原主應用下線;迅速的主備切換可以有效的縮短故障時間
基于上面的描述,主備架構特點比較清晰
- 采用備援的方案,加一台備用服務
- 缺點就是資源浪費
其次就是這個架構模型最需要考慮的則是如何實作主備切換?
- 人工
- VIP(虛拟ip) + keepalived 機制
1.2 主從架構
主從一般又叫做讀寫分離,主提供讀寫能力,而從則隻提供讀能力
鑒于當下的網際網路應用,絕大多數都是讀多寫少的場景;讀更容易成為性能瓶頸,是以采用讀寫分離,可以有效的提高整個叢集的響應能力
主從架構可以區分為:一主多從 + 一主一從再多從,以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協定詳解 - 騰訊雲開發者社群-騰訊雲
- P2P 網絡核心技術:Gossip 協定 - 知乎
- 從Paxos到Raft,分布式一緻性算法解析_mb5fdb0a87e2fa1的技術部落格_51CTO部落格
- 【理論篇】淺析分布式中的 CAP、BASE、2PC、3PC、Paxos、Raft、ZAB - 知乎
2.1 CAP定理
CAP 定理指出,分布式系統 不可能 同時提供下面三個要求:
- Consistency:一緻性
- 操作更新完成并傳回用戶端之後,所有節點資料完全一緻
- Availability:可用性
- 服務一直可用
- Partition tolerance:分區容錯性
- 分布式系統在遇到某節點或網絡分區故障的時候,仍然能夠對外提供滿足一緻性和可用性的服務
通常來講P很難不保證,當服務部署到多台執行個體上時,節點異常、網絡故障屬于常态,根據不同業務場景進行選擇
對于服務有限的應用而言,首選AP,保證高可用,即使部分機器異常,也不會導緻整個服務不可用;如絕大多數的前台應用都是這種
對于資料一緻性要求高的場景,如涉及到錢的支付結算,CP可能更重要了
對于CAP的三種組合說明如下
選擇 | 說明 |
CA | 放棄分區容錯性,加強一緻性和可用性,其實就是傳統的單機場景 |
AP | 放棄一緻性(這裡說的一緻性是強一緻性),追求分區容錯性和可用性,這是很多分布式系統設計時的選擇,例如很多NoSQL系統就是如此 |
CP | 放棄可用性,追求一緻性和分區容錯性,基本不會選擇,網絡問題會直接讓整個系統不可用 |
2.2 BASE理論
base理論作為cap的延伸,其核心特點在于放棄強一緻性,追求最終一緻性
- Basically Available: 基本可用
- 指分布式系統在出現故障的時候,允許損失部分可用性,即保證核心可用
- 如大促時降級政策
- Soft State:軟狀态
- 允許系統存在中間狀态,而該中間狀态不會影響系統整體可用性
- MySql異步方式的主從同步,可能導緻的主從資料不一緻
- Eventual Consistency:最終一緻性
- 最終一緻性是指系統中的所有資料副本經過一定時間後,最終能夠達到一緻的狀态
基于上面的描述,可以看到BASE理論适用于大型高可用可擴充的分布式系統
注意其不同于ACID的強一緻性模型,而是通過犧牲強一緻性 來獲得可用性,并允許資料在一段時間内是不一緻的,但最終達到一緻狀态
2.3 PACELEC 定理
這個真沒聽說過,以下内容來自:
- Distributed System Design Patterns | by Nishant | Medium
- 如果有一個分區(‘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 算法動畫示範
- Raft算法詳解 - 知乎
為了解決paxos的複雜性,raft算法提供了一套更易了解的算法基礎,其核心流程在于:
leader接受請求,并轉發給follow,當大部分follow響應之後,leader通知所有的follow送出請求、同時自己也送出請求并告訴調用方ok
角色劃分:
- Leader:上司者,接受用戶端請求,并向Follower同步請求,當資料同步到大多數節點上後告訴Follower送出日志
- Follow: 接受并持久化Leader同步的資料,在Leader告之日志可以送出之後,送出
- Candidate:Leader選舉過程中的臨時角色,向其他節點拉選票,得到多數的晉升為leader,選舉完成之後不存在這個角色
2.6 ZAB協定
ZAB(Zookeeper Atomic Broadcast) 協定是為分布式協調服務ZooKeeper專門設計的一種支援崩潰恢複的一緻性協定,基于該協定,ZooKeeper 實作了一種 主從模式的系統架構來保持叢集中各個副本之間的資料一緻性。
- zookeeper核心之ZAB協定就這麼簡單!
主要用于zk的資料一緻性場景,其核心思想是Leader再接受到事務請求之後,通過給Follower,當半數以上的Follower傳回ACK之後,Leader送出提案,并向Follower發送commit資訊
角色劃分
- Leader: 負責整個Zookeeper 叢集工作機制中的核心
- 事務請求的唯一排程和處理者,保證叢集事務處理的順序性
- 叢集内部各伺服器的排程者
- Follower:Leader的追随者
- 處理用戶端的非實物請求,轉發事務請求給 Leader 伺服器
- 參與事務請求 Proposal 的投票
- 參與 Leader 選舉投票
- Observer:是 zookeeper 自 3.3.0 開始引入的一個角色,
- 它不參與事務請求 Proposal 的投票,
- 也不參與 Leader 選舉投票
- 隻提供非事務的服務(查詢),通常在不影響叢集事務處理能力的前提下提升叢集的非事務處理能力。
2.7 2PC協定
two-phase commit protocol,兩階段送出協定,主要是為了解決強一緻性,中心化的強一緻性協定
角色劃分
- 協調節點(coordinator):中心化
- 參與者節點(partcipant):多個
執行流程
協調節點接收請求,然後向參與者節點送出
precommit
,當所有的參與者都回複ok之後,協調節點再給所有的參與者節點送出
commit
,所有的都傳回ok之後,才表明這個資料确認送出
當第一個階段,有一個參與者失敗,則所有的參與者節點都復原
特點
優點在于實作簡單
缺點也很明顯
- 協調節點的單點故障
- 第一階段全部ack正常,第二階段存在部分參與者節點異常時,可能出現不一緻問題
2.8 3PC協定
分布式事務:兩階段送出與三階段送出 - SegmentFault 思否
在兩階段的基礎上進行擴充,将第一階段劃分兩部,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 協定 - 知乎
主要用在分布式資料庫系統中各個副本節點同步資料之用,這種場景的一個最大特點就是組成的網絡的節點都是對等節點,是非結構化網絡
工作流程
- 周期性的傳播消息,通常周期時間為1s
- 被感染的節點,随機選擇n個相鄰節點,傳播消息
- 每次傳播消息都選擇還沒有發送過的節點進行傳播
- 收單消息的節點,不會傳播給向它發送消息的節點
特點
- 擴充性:允許節點動态增加、減少,新增的節點狀态最終會與其他節點一緻
- 容錯:網絡中任意一個節點當機重新開機都不會影響消息傳播
- 去中心化:不要求中心節點,所有節點對等,任何一個節點無需知道整個網絡狀況,隻要網絡連通,則一個節點的消息最終會散播到整個網絡
- 一緻性收斂:協定中的消息會以一傳十、十傳百一樣的指數級速度在網絡中快速傳播,是以系統狀态的不一緻可以在很快的時間内收斂到一緻。消息傳播速度達到了 logN
- 簡單:Gossip 協定的過程極其簡單,實作起來幾乎沒有太多複雜性
缺點
- 消息延遲:節點隻會随機向少數幾個節點發送消息,消息最終是通過多個輪次的散播而到達全網的,是以使用 Gossip 協定會造成不可避免的消息延遲
- 消息備援:節點會定期随機選擇周圍節點發送消息,而收到消息的節點也會重複該步驟,導緻消息的備援
2.10 一灰灰的小結
本節主要介紹的是分布式系統設計中的一些常見的理論基石,如分布式中如何保障一緻性,如何對一個提案達成共識
- BASE,CAP,PACELEC理論:建構穩定的分布式系統應該考慮的方向
- paxos,raft共識算法
- zab一緻性協定
- gossip消息同步協定
3.算法
這一節将主要介紹下分布式系統中的經典的算法,比如常用于分區的一緻性hash算法,适用于一緻性的Quorum NWR算法,PBFT拜占庭容錯算法,區塊鍊中大量使用的工作量證明PoW算法等
3.1 一緻性hash算法
一緻性hash算法,主要應用于資料分片場景下,有效降低服務的新增、删除對資料複制的影響
通過對資料項的鍵進行哈希處理映射其在環上的位置,然後順時針周遊環以查找位置大于該項位置的第一個節點,将每個由鍵辨別的資料配置設定給hash環中的一個節點
一緻散列的主要優點是增量穩定性; 節點添加删除,對整個叢集而言,僅影響其直接鄰居,其他節點不受影響。
注意:
- redis叢集實作了一套hash槽機制,其核心思想與一緻性hash比較相似
3.2 Quorum NWR算法
用來保證資料備援和最終一緻性的投票算法,其主要數學思想來源于鴿巢原理
- 分布式系統之Quorum (NRW)算法-阿裡雲開發者社群
- 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 - 知乎
- 詳解CQRS架構模式_架構_Kislay Verma_InfoQ精選文章
Command Query Responsibility Segregation 即我們通俗了解的讀寫分離,其核心思想在于将兩類不同操作進行分離,在獨立的服務中實作
用途在于将領域模型與查詢功能進行分離,讓一些複雜的查詢擺脫領域模型的限制,以更為簡單的 DTO 形式展現查詢結果。同時分離了不同的資料存儲結構,讓開發者按照查詢的功能與要求更加自由的選擇資料存儲引擎
4.2 複制負載平衡服務
- 分布式系統設計:服務模式之複制負載平衡服務 - 知乎
- 負載均衡排程算法大全 | 菜鳥教程
複制負載平衡服務(Replication Load Balancing Service, RLBS),可以簡單了解為我們常說的負載均衡,多個相同的服務執行個體建構一個叢集,每個服務都可以響應請求,負載均衡器負責請求的分發到不同的執行個體上,常見的負載算法
算法 | 說明 | 特點 |
輪詢 | 請求按照順序依次分發給對應的伺服器 | 優點簡單,缺點在于未考慮不同伺服器的實際性能情況 |
權重輪詢 | 權重高的被分發更多的請求 | 優點:充分利用機器的性能 |
最少連接配接數 | 找連接配接數最少的伺服器進行請求分發,若所有伺服器相同的連接配接數,則找第一個選擇的 | 目的是讓優先讓空閑的機器響應請求 |
少連接配接數慢啟動時間 | 剛啟動的伺服器,在一個時間段内,連接配接數是有限制且緩慢增加 | 避免剛上線導緻大量的請求分發過來而超載 |
權重最少連接配接 | 平衡服務性能 + 最少連接配接數 | |
基于代理的自适應負載均衡 | 載主機包含一個自适用邏輯用來定時監測伺服器狀态和該伺服器的權重 | |
源位址哈希法 | 擷取用戶端的IP位址,通過哈希函映射到對應的伺服器 | 相同的來源請求都轉發到相同的伺服器上 |
随機 | 随機算法選擇一台伺服器 | |
固定權重 | 最高權重隻有在其他伺服器的權重值都很低時才使用。然而,如果最高權重的伺服器下降,則下一個最高優先級的伺服器将為用戶端服務 | 每個真實伺服器的權重需要基于伺服器優先級來配置 |
權重響應 | 伺服器響應越小其權重越高,通常是基于心跳來判斷機器的快慢 | 心跳的響應并不一定非常準确反應服務情況 |
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
這一節主要簡單的介紹了下分布式系統中應用到的一些技術方案,如有對其中某個技術有興趣的小夥伴可以留言,後續會逐一進行補全
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
- 從0到1實作一個分布式鎖 | 一灰灰Learning
- 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 題外話
- 鹹魚太久了,想做一些有意思的東西,活躍一下大腦
- 準備依托于《分布式專欄》來将自己的知識體系進行歸納彙總,讓零散分布在大腦中的知識點能有一個脈絡串聯起來
- 不想做架構的碼農不是好碼農,而想成為一個好的架構,當然得做一些基礎準備,向業務精品學習取經