傳統單體服務 | 分佈式微服務 | |
---|---|---|
新功能開發 | 複雜 | 工做量少、容易實現 |
架構設計 | 難度小 | 難度大 |
系統性能 | 相應時間快,吞吐量小 | 相應時間慢、吞吐量大 |
系統運維 | 運維簡單 | 運維複雜 |
技術 | 技術單一且封閉 | 技術多樣且開放 |
測試和查錯 | 簡單 | 複雜 |
擴展性 | 擴展性不好 | 擴展性很是好 |
分佈式提升了系統可用性、吞吐量,加強了系統擴展性,但同時也帶來了一些問題。好比增長了系統複雜性、排查問題的難度、服務之間調度產生的各類問題,最大的問題就是數據一致性。
某一件事情分配給了多個不一樣的服務取實現,因爲網絡傳輸問題,只要有任何一個服務執行失敗則整件事情就失敗了,其餘已經執行成功的服務就產生了髒數據,形成了數據不一致。這是分佈式帶來的、不可避免的問題。業界對這個問題作了許多研究,提出了多種解決方案。面試
CAP定理又稱CAP原則,指的是在一個分佈式系統中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分區容錯性),最多隻能同時三個特性中的兩個,三者不可兼得。
算法
描述 | |
---|---|
Consistency(一致性) | 指數據在多個副本之間可以保持一致的特性(嚴格的一致性) |
Availability(可用性) | 指系統提供的服務必須一直處於可用的狀態,每次請求都能獲取到非錯的響應(不保證獲取的數據爲最新數據) |
Partition tolerance(分區容錯性) | 分佈式系統在遇到任何網絡分區故障的時候,仍然可以對外提供知足一致性和可用性的服務,除非整個網絡環境都發生了故障 |
什麼是分區?數據庫
在分佈式系統中,不一樣的節點分佈在不一樣的子網絡中,因爲一些特殊的緣由,這些子節點之間出現了網絡不通的狀態,但他們的內部子網絡是正常的。從而致使了整個系統的環境被切分紅了若干個孤立的區域,同時每一個孤立的區域可以對外提供完整的功能,這就是分區。服務器
經過CAP理論,咱們知道沒法同時知足一致性、可用性和分區容錯性這三個特性,那要捨棄哪一個呢?markdown
若是不要求P(不容許分區),即單一系統或者全部微服務都在一個網絡內,好比一臺邏輯服務器或者虛擬機少給你,則C(強一致性)和A(可用性)是能夠保證的。好比單一數據庫、單一服務。可是分佈式系統分區必定存在分區,所以P必定須要考慮。因此CAP理論實際上是容許P以後,考慮如何盡最大努力保證C和A。網絡
若是不要求A(可用),至關於每一個請求都須要在Server之間強一致,而P(分區)會致使同步時間無限延長,如此CP也是能夠保證的。不少傳統的數據庫分佈式事務都屬於這種模式,當發生C或者P的時候,系統不可用,必須強制確保C和P。架構
要高可用並容許分區,則需放棄一致性。一旦分區發生,節點之間可能會失去聯繫,爲了高可用,每一個節點只能用本地數據提供服務,而這樣會致使全局數據的不一致性,可是整個系統對外展現仍是可用的,若是測試有數據修改則更會形成數據不一直,所以這個時候可用對系統作一些控制,好比容許只讀,不容許寫數據,至關於下降了部分A的功能,來確保數據不一致程度最小。還有一種方法就是在各個分區內記錄下寫的操做,等P回覆以後再同步數據,確保數據最終一致性,可是這樣有風險。併發
上面三種權衡策略有各自的優缺點,須要根據不一樣的場景選擇不一樣的權衡策略。好比對於普通的、影響不大的應用,可用選擇A P without C,捨棄數據一致性來確保系統可用;可是對於金融、支付系統必須強制確保C,當發生了P的時候,可用捨棄A,即寧肯系統不可用,也要保證數據強一致性。
CAP理論之間的關係以下圖:
ACID就是C&A without P, 對於單一數據庫,沒有P,所以單一數據庫可用保證ACID特性。
BASE就是犧牲了數據強一致性,保證了A&P,但它能確保數據最終一致性。
運維
BASE理論是Basically Available(基本可用),Soft State(軟狀態)和Eventually Consistent(最終一致性)三個短語的縮寫。
分佈式
核心思想
既是沒法作到強一致性(Strong consistency),但每一個應用均可以根據自身的業務特色,採用適當的方式來使系統達到最終一致性(Eventual consistency)。
什麼是基本可用呢?就是發生了P,犧牲C,還能保證A。
假設系統,出現了不可預知的故障,但仍是能用,相比較正常的系統而言:
什麼是軟狀態呢?相對於原子性而言,要求多個節點的數據副本都是一致的,這是一種「硬狀態」。
軟狀態指的是:容許系統中的數據存在中間狀態,並認爲該狀態不影響系統的總體可用性,即容許系統在多個不一樣節點的數據副本存在數據延時。
上面說軟狀態,而後不可能一直是軟狀態,必須有個時間期限。在期限事後,應當保證全部副本保持數據一致性,從而達到數據的最終一致性。這個時間期限取決於網絡延時、系統負載、數據複製方案設計等等因素。
實際上,不僅是分佈式系統使用最終一致性,關係型數據庫在某個功能上,也是使用最終一致性的。好比備份,數據庫的複製過程是須要時間的,這個複製過程當中,業務讀取到的值就是舊的。固然,最終仍是達成了數據一致性。這也算是一個最終一致性的經典案例。
分佈式涉及到數據一致性問題,數據庫中經過事務確保數據一致性。所以分佈式系統也可用經過分佈式事務來確保分佈式數據一致性。
2PC(Two-phase commit protocol),中文叫二階段提交。 二階段提交是一種強一致性設計,2PC 引入一個事務協調者的角色來協調管理各參與者(也可稱之爲各本地資源)的提交和回滾,二階段分別指的是準備和提交兩個階段。
注意這只是協議或者說是理論指導,只闡述了大方向,具體落地仍是有會有差別的。
讓咱們來看下兩個階段的具體流程。
假如在第一階段全部參與者都返回準備成功,那麼協調者則向全部參與者發送提交事務命令,而後等待全部事務都提交成功以後,返回事務執行成功。
讓咱們來看一下流程圖:
假如在第一階段有一個參與者返回失敗,那麼協調者就會向全部參與者發送回滾事務的請求,即分佈式事務執行失敗
那可能就有人問了,那第二階段提交失敗的話呢?
這裏有兩種狀況:
大致上二階段提交的流程就是這樣,咱們再來看看細節。
首先 2PC 是一個同步阻塞協議,像第一階段協調者會等待全部參與者響應纔會進行下一步操做,固然第一階段的協調者有超時機制,假設由於網絡緣由沒有收到某參與者的響應或某參與者掛了,那麼超時後就會判斷事務失敗,向全部參與者發送回滾命令。
在第二階段協調者的無法超時,由於按照咱們上面分析只能不斷重試!
2PC 是一種儘可能保證強一致性的分佈式事務,所以它是同步阻塞的,而同步阻塞就致使長久的資源鎖定問題,整體而言效率低,而且存在單點故障問題,在極端條件下存在數據不一致的風險。
3PC 的出現是爲了解決 2PC 的一些問題,相比於 2PC 它在參與者中也引入了超時機制,而且新增了一個階段使得參與者能夠利用這一個階段統一各自的狀態。
3PC 包含了三個階段,分別是準備階段、預提交階段和提交階段,對應的英文就是:CanCommit、PreCommit 和 DoCommit。
讓咱們來看一下圖:
無論哪個階段有參與者返回失敗都會宣佈事務失敗,這和 2PC 是同樣的(固然到最後的提交階段和 2PC 同樣只要是提交請求就只能不斷重試)。
相對於2PC, 3PC多引入一個階段也多一個交互,所以性能會差一些,並且絕大部分的狀況下資源應該都是可用的,這樣等於每次明知可用執行還得詢問一次。
那麼引入了超時機制,參與者就不會傻等了,若是是等待提交命令超時,那麼參與者就會提交事務了,由於都到了這一階段了大機率是提交的,若是是等待預提交命令超時,那該幹啥就幹啥了,反正原本啥也沒幹。
然而超時機制也會帶來數據不一致的問題,好比在等待提交命令時候超時了,參與者默認執行的是提交事務操做,可是有可能執行的是回滾操做,這樣一來數據就不一致了.
3PC 的引入是爲了解決提交階段 2PC 協調者和某參與者都掛了以後新選舉的協調者不知道當前應該提交仍是回滾的問題。
新協調者來的時候發現有一個參與者處於預提交或者提交階段,那麼代表已經通過了全部參與者的確認了,因此此時執行的就是提交命令。
因此說 3PC 就是經過引入預提交階段來使得參與者之間的狀態獲得統一,也就是留了一個階段讓你們同步一下。
可是這也只能讓協調者知道該若是作,但不能保證這樣作必定對,這其實和上面 2PC 分析一致,由於掛了的參與者到底有沒有執行事務沒法判定。
因此說 3PC 經過預提交階段能夠減小故障恢復時候的複雜性,可是不能保證數據一致,除非掛了的那個參與者恢復。
讓咱們總結一下, 3PC 相對於 2PC 作了必定的改進:引入了參與者超時機制,而且增長了預提交階段使得故障恢復以後協調者的決策複雜度下降,但總體的交互過程更長了,性能有所降低,而且仍是會存在數據不一致問題。
因此 2PC 和 3PC 都不能保證數據100%一致,所以通常都須要有定時掃描補償機制
2PC 和 3PC 都是數據庫層面的,而 TCC 是業務層面的分佈式事務,就像我前面說的分佈式事務不只僅包括數據庫的操做,還包括髮送短信等,這時候 TCC 就派上用場了!
TCC 指的是Try - Confirm - Cancel。
其實從思想上看和 2PC 差很少,都是先試探性的執行,若是均可以那就真正的執行,若是不行就回滾。
好比說一個事務要執行A、B、C三個操做,那麼先對三個操做執行預留動做。若是都預留成功了那麼就執行確認操做,若是有一個預留失敗那就都執行撤銷動做。
咱們來看下流程,TCC模型還有個事務管理者的角色,用來記錄TCC全局事務狀態並提交或者回滾事務。
能夠看到流程仍是很簡單的,難點在於業務上的定義,對於每個操做你都須要定義三個動做分別對應Try - Confirm - Cancel。
所以 TCC 對業務的侵入較大和業務緊耦合,須要根據特定的場景和業務邏輯來設計相應的操做。
還有一點要注意,撤銷和確認操做的執行可能須要重試,所以還須要保證操做的冪等。
相對於 2PC、3PC ,TCC 適用的範圍更大,可是開發量也更大,畢竟都在業務上實現,並且有時候你會發現這三個方法還真很差寫。不過也由於是在業務上實現的,因此TCC能夠跨數據庫、跨不一樣的業務系統來實現事務。
本地消息表其實就是利用了 各系統本地的事務來實現分佈式事務。
本地消息表顧名思義就是會有一張存放本地消息的表,通常都是放在數據庫中,而後在執行業務的時候 將業務的執行和將消息放入消息表中的操做放在同一個事務中,這樣就能保證消息放入本地表中業務確定是執行成功的。
而後再去調用下一個操做,若是下一個操做調用成功了好說,消息表的消息狀態能夠直接改爲已成功。
若是調用失敗也沒事,會有 後臺任務定時去讀取本地消息表,篩選出還未成功的消息再調用對應的服務,服務更新成功了再變動消息的狀態。
這時候有可能消息對應的操做不成功,所以也須要重試,重試就得保證對應服務的方法是冪等的,並且通常重試會有最大次數,超過最大次數能夠記錄下報警讓人工處理。
能夠看到本地消息表其實實現的是最終一致性,容忍了數據暫時不一致的狀況。
RocketMQ 就很好的支持了消息事務,讓咱們來看一下如何經過消息實現事務。
第一步先給 Broker 發送事務消息即半消息,半消息不是說一半消息,而是這個消息對消費者來講不可見,而後發送成功後發送方再執行本地事務。
再根據本地事務的結果向 Broker 發送 Commit 或者 RollBack 命令。
而且 RocketMQ 的發送方會提供一個反查事務狀態接口,若是一段時間內半消息沒有收到任何操做請求,那麼 Broker 會經過反查接口得知發送方事務是否執行成功,而後執行 Commit 或者 RollBack 命令。
若是是 Commit 那麼訂閱方就能收到這條消息,而後再作對應的操做,作完了以後再消費這條消息便可。
若是是 RollBack 那麼訂閱方收不到這條消息,等於事務就沒執行過。
能夠看到經過 RocketMQ 仍是比較容易實現的,RocketMQ 提供了事務消息的功能,咱們只須要定義好事務反查接口便可。
能夠看到消息事務實現的也是最終一致性。消息事務與本地消息表差很少,只是它的小i西保存在消息隊列。
【1】分佈式理論(一) - CAP定理
【2】分佈式理論(二) - BASE理論
【3】分佈式理論(三) - 2PC協議
【4】分佈式理論(四) - 3PC協議
【5】分佈式理論(五) - 一致性算法Paxos
【6】分佈式理論(六) - 一致性協議Raft
【7】面試必問:分佈式事務六種解決方案
【8】一分鐘弄懂什麼是分佈式和微服務
【9】分佈式優缺點