分散式從 ACID、CAP、BASE 的理論推進

aceld發表於2020-03-27

原創宣告 作者: 劉丹冰 Aceld,微信公眾號同名

作為當今網際網路後端技術棧工程師、無論 Golang、Java 或者其他系,分散式的理論概念都逐步成為必備理論基礎知識之一, 本文主要討論分散式的 CAP 理論的推進,這是你走進分散式大門的第一塊敲門磚。

提綱: 一、從本地事務到分散式理論 二、ACID 理論 三、CAP 理論 四、CAP 理論 “3 選 2” 論證 五、BASE 理論


附加:分散式概念 分散式實際上就是單一的本地一體解決方案,在硬體或者資源上不夠業務需求,而採取的一種分散式多節點,可以擴容資源的一種解決思路。它研究如何把一個需要非常巨大的計算能力才能解決的問題分成許多小的部分,然後把這些部分分配給多個計算機進行處理,最後把這些計算結果綜合起來得到最終的結果。


那麼在瞭解分散式之前,我們應該從一體式的構造開始說明。

一、從本地事務到分散式理論

理解第一個問題就是"事務"

事務提供一種機制將一個活動涉及的所有操作納入到一個不可分割的執行單元,組成事務的所有操作只有在所有操作均能正常執行的情況下方能提交,只要其中任一操作執行失敗,都將導致整個事務的回滾。

簡單地說,事務提供一種 “ 要麼什麼都不做,要麼做全套(All or Nothing)” 機制。

二、ACID 理論

​ 事務是基於資料進行操作,需要保證事務的資料通常儲存在資料庫中,所以介紹到事務,就不得不介紹資料庫事務的ACID特性,指資料庫事務正確執行的四個基本特性的縮寫。包含:

  • 原子性(Atomicity)

  • 一致性(Consistency)

  • 隔離性(Isolation)

  • 永續性(Durability)

(1) 原子性(Atomicity)

​ 整個事務中的所有操作,要麼全部完成,要麼全部不完成,不可能停滯在中間某個環節。

例如:銀行轉賬,從 A 賬戶轉 100 元至 B 賬戶:

A、從 A 賬戶取 100 元

B、存入 100 元至 B 賬戶。 這兩步要麼一起完成,要麼一起不完成,如果只完成第一步,第二步失敗,錢會莫名其妙少了 100 元。

(2) 一致性(Consistency)

在事務開始之前和事務結束以後,資料庫資料的一致性約束沒有被破壞。

例如:現有完整性約束 A+B=100,如果一個事務改變了 A,那麼必須得改變 B,使得事務結束後依然滿足 A+B=100,否則事務失敗。

(3) 隔離性(Isolation)

​ 資料庫允許多個併發事務同時對資料進行讀寫和修改的能力,如果一個事務要訪問的資料正在被另外一個事務修改,只要另外一個事務未提交,它所訪問的資料就不受未提交事務的影響。隔離性可以防止多個事務併發執行時由於交叉執行而導致資料的不一致。

例如:現有有個交易是從 A 賬戶轉 100 元至 B 賬戶,在這個交易事務還未完成的情況下,如果此時 B 查詢自己的賬戶,是看不到新增加的 100 元的。

(4) 永續性(Durability)

​ 事務處理結束後,對資料的修改就是永久的,即便系統故障也不會丟失。

​ 本地事務 ACID 實際上可用” 統一提交,失敗回滾 “幾個字總結,嚴格保證了同一事務內資料的一致性!

而分散式事務不能實現這種ACID。因為有 CAP 理論約束。接下來我們來了解一下,分散式中是如何保證以上特性的,那麼就有了一個著名的 CAP 理論。


三、CAP 理論

​ 在設計一個大規模可擴放的網路服務時候會遇到三個特性:一致性(consistency)、可用性(Availability)、分割槽容錯(partition-tolerance)都需要的情景.

​ CAP 定律說的是在一個分散式計算機系統中,一致性,可用性和分割槽容錯性這三種保證無法同時得到滿足,最多滿足兩個。

如上圖,CAP 的三種特性只能同時滿足兩個。而且在不同的兩兩組合,也有一些成熟的分散式產品。

接下來,我們來介紹一下 CAP 的三種特性,我們採用一個應用場景來分析 CAP 中的每個特點的含義。

該場景整體分為 5 個流程:

流程一、客戶端傳送請求 (如:新增訂單、修改訂單、刪除訂單)

流程二、Web 業務層處理業務,並修改儲存成資料資訊

流程三、儲存層內部 Master 與 Backup 的資料同步

流程四、Web 業務層從儲存層取出資料

流程五、Web 業務層返回資料給客戶端

(1) 一致性 Consistency

all nodes see the same data at the same time

一旦資料更新完成併成功返回客戶端後,那麼分散式系統中所有節點在同一時間的資料完全一致。

在 CAP 的一致性中還包括強一致性、弱一致性、最終一致性等級別,稍後我們在後續章節介紹。

一致性是指寫操作後的讀操作可以讀取到最新的資料狀態,當資料分佈在多個節點上,從任意結點讀取到的資料都是最新的狀態。

一致性實現目標:

  • Web 業務層向主 Master 寫資料庫成功,從 Backup 讀資料也成功。

  • Web 業務層向主 Master 讀資料庫失敗,從 Backup 讀資料也失敗。

必要實現流程:

寫入主資料庫後,在向從資料庫同步期間要將從資料庫鎖定,待同步完成後再釋放鎖,以免在新資料寫入成功後,向從資料庫查詢到舊的資料。

分散式一致性特點:

  1. 由於存在資料同步的過程,寫操作的響應會有一定的延遲。
  2. 為了保證資料一致性會對資源暫時鎖定,待資料同步完成釋放鎖定資源。
  3. 如果請求資料同步失敗的結點則會返回錯誤資訊,一定不會返回舊資料。

(2) 可用性 (Availability)

Reads and writes always succeed

服務一直可用,而且是正常響應時間。

對於可用性的衡量標準如下:

可用性分類 可用水平(%) 一年中可容忍停機時間
容錯可用性 99.9999 <1 min
極高可用性 99.999 <5 min
具有故障自動恢復能力的可用性 99.99 <53 min
高可用性 99.9 <8.8h
商品可用性 99 <43.8 min

可用性實現目標:

  • 當 Master 正在被更新,Backup 資料庫接收到資料查詢的請求則立即能夠響應資料查詢結果。

  • backup 資料庫不允許出現響應超時或響應錯誤。

必要實現流程:

  1. 寫入 Master 主資料庫後要將資料同步到從資料庫。

  2. 由於要保證 Backup 從資料庫的可用性,不可將 Backup 從資料庫中的資源進行鎖定。

  3. 即時資料還沒有同步過來,從資料庫也要返回要查詢的資料,哪怕是舊資料/或者預設資料,但不能返回錯誤或響應超時。

分散式可用性特點:

所有請求都有響應,且不會出現響應超時或響應錯誤。

(3) 分割槽容錯性 (Partition tolerance)

the system continues to operate despite arbitrary message loss or failure of part of the system

分散式系統中,儘管部分節點出現任何訊息丟失或者故障,系統應繼續執行。

通常分散式系統的各各結點部署在不同的子網,這就是網路分割槽,不可避免的會出現由於網路問題而導致結點之間通訊失敗,此時仍可對外提供服務。

分割槽容錯性實現目標:

  • 主資料庫向從資料庫同步資料失敗不影響讀寫操作。

  • 其一個結點掛掉不影響另一個結點對外提供服務。

必要實現流程:

  1. 儘量使用非同步取代同步操作,例如使用非同步方式將資料從主資料庫同步到從資料,這樣結點之間能有效的實現鬆耦合。
  2. 新增 Backup 從資料庫結點,其中一個 Backup 從結點掛掉其它 Backup 從結點提供服務。

分割槽容錯性特點:

分割槽容忍性分是布式系統具備的基本能力。

四、CAP 的” 3 選 2“證明

(1) 基本場景

在小結中,我們主要介紹 CAP 的理論為什麼不能夠 3 個特性同時滿足。

如上圖,是我們證明 CAP 的基本場景,分散式網路中有兩個節點 Host1 和 Host2,他們之間網路可以連通,Host1 中執行 Process1 程式和對應的資料庫 Data,Host2 中執行 Process2 程式和對應資料庫 Data。

(2) CAP 特性

如果滿足一致性(C):那麼Data(0) = Data(0).

如果滿足可用性(A): 使用者不管請求 Host1 或 Host2,都會立刻響應結果。

如果滿足分割槽容錯性(P): Host1 或 Host2 有一方脫離系統 (故障), 都不會影響 Host1 和 Host2 彼此之間正常運作。

(3) 分散式系統正常執行流程

如上圖,是分散式系統正常運轉的流程。

A、使用者向Host1主機請求資料更新,程式Process1更新資料庫Data(0)Data(1)

B、分散式系統將資料進行同步操作,將Host1中的Data(1)同步的Host2中`Data(0),使Host2中的資料也變為Data(1)

C、當使用者請求主機Host2時,則Process2則響應最新的Data(1)資料

根據 CAP 的特性:

  • Host1Host2的資料庫Data之間的資料是否一樣為一致性 (C)

  • 使用者對Host1Host2的請求響應為可用性 (A)

  • Host1Host2之間的各自網路環境為分割槽容錯性 (P)

當前是一個正常運作的流程,目前 CAP 三個特性可以同時滿足,也是一個理想狀態,但是實際應用場景中,發生錯誤在所難免,那麼如果發生錯誤 CAP 是否能同時滿足,或者該如何取捨?


(4) 分散式系統異常執行流程

假設Host1Host2之間的網路斷開了,我們要支援這種網路異常,相當於要滿足分割槽容錯性(P),能不能同時滿足一致性(C)可用響應性(A)呢?

假設在 N1 和 N2 之間網路斷開的時候,

A、使用者向Host1傳送資料更新請求,那Host1中的資料Data(0)將被更新為Data(1)

B、弱此時Host1Host2網路是斷開的,所以分散式系統同步操作將失敗,Host2中的資料依舊是Data(0)

C、有使用者向Host2傳送資料讀取請求,由於資料還沒有進行同步,Process2沒辦法立即給使用者返回最新的資料 V1,那麼將面臨兩個選擇。

第一,犧牲資料一致性(c),響應舊的資料Data(0)給使用者;

第二,犧牲可用性(A),阻塞等待,直到網路連線恢復,資料同步完成之後,再給使用者響應最新的資料Data(1)

這個過程,證明了要滿足分割槽容錯性(p)的分散式系統,只能在一致性(C)可用性(A)兩者中,選擇其中一個。

(5) "3 選 2"的必然性

通過 CAP 理論,我們知道無法同時滿足一致性可用性分割槽容錯性這三個特性,那要捨棄哪個呢?

CA 放棄 P:

一個分散式系統中,不可能存在不滿足 P,放棄分割槽容錯性(p),即不進行分割槽,不考慮由於網路不通或結點掛掉的問題,則可以實現一致性和可用性。那麼系統將不是一個標準的分散式系統。我們最常用的關係型資料就滿足了 CA,如下:

主資料庫和從資料庫中間不再進行資料同步,資料庫可以響應每次的查詢請求,通過事務 (原子性操作) 隔離級別實現每個查詢請求都可以返回最新的資料。

注意:

對於一個分散式系統來說。P 是一個基本要求,CAP 三者中,只能在 CA 兩者之間做權衡,並且要想盡辦法提升 P。

CP 放棄 A

如果一個分散式系統不要求強的可用性,即容許系統停機或者長時間無響應的話,就可以在 CAP 三者中保障 CP 而捨棄 A。

放棄可用性,追求一致性和分割槽容錯性,如 Redis、HBase 等,還有分散式系統中常用的 Zookeeper 也是在 CAP 三者之中選擇優先保證 CP 的。

場景:

跨行轉賬,一次轉賬請求要等待雙方銀行系統都完成整個事務才算完成。

AP 放棄 C

放棄一致性,追求分割槽容忍性和可用性。這是很多分散式系統設計時的選擇。實現 AP,前提是隻要使用者可以接受所查詢的到資料在一定時間內不是最新的即可。

通常實現 AP 都會保證最終一致性,後面講的 BASE 理論就是根據 AP 來擴充套件的。

場景 1

淘寶訂單退款。今日退款成功,明日賬戶到賬,只要使用者可以接受在一定時間內到賬即可。

場景 2:

12306 的買票。都是在可用性和一致性之間捨棄了一致性而選擇可用性。

在 12306 買票的時候提示有票(但是可能實際已經沒票了),使用者正常去輸入驗證碼,下單。但是過了一會系統提示下單失敗,餘票不足。這其實就是先在可用性方面保證系統可以正常的服務,然後在資料的一致性方面做了些犧牲,會影響一些使用者體驗,但是也不至於造成使用者流程的嚴重阻塞。

但是,我們說很多網站犧牲了一致性,選擇了可用性,這其實也不準確的。就比如上面的買票的例子,其實捨棄的只是強一致性。退而求其次保證了最終一致性。也就是說,雖然下單的瞬間,關於車票的庫存可能存在資料不一致的情況,但是過了一段時間,還是要保證最終一致性的。

(6) 總結:

CA 放棄 P:如果不要求 P(不允許分割槽),則 C(強一致性)和 A(可用性)是可以保證的。這樣分割槽將永遠不會存在,因此 CA 的系統更多的是允許分割槽後各子系統依然保持 CA。

CP 放棄 A:如果不要求 A(可用),相當於每個請求都需要在 Server 之間強一致,而 P(分割槽)會導致同步時間無限延長,如此 CP 也是可以保證的。很多傳統的資料庫分散式事務都屬於這種模式。

AP 放棄 C:要高可用並允許分割槽,則需放棄一致性。一旦分割槽發生,節點之間可能會失去聯絡,為了高可用,每個節點只能用本地資料提供服務,而這樣會導致全域性資料的不一致性。現在眾多的 NoSQL 都屬於此類。

五、思考

思考:按照 CAP 理論如何設計一個電商系統?

  • 首先個電商網站核心模組有使用者,訂單,商品,支付,促銷管理

1、對於使用者模組,包括登入,個人設定,個人訂單,購物車,收藏夾等,這些模組保證 AP,資料短時間不一致不影響使用。 2、訂單模組的下單付款扣減庫存操作是整個系統的核心,CA 都需要保證,極端情況下面犧牲 A 保證 C 3、商品模組的商品上下架和庫存管理保證 CP 4、搜尋功能因為本身就不是實時性非常高的模組,所以保證 AP 就可以了。 5、促銷是短時間的資料不一致,結果就是優惠資訊看不到,但是已有的優惠要保證可用,而且優惠可以提前預計算,所以可以保證 AP。 6、支付這一塊是獨立的系統,或者使用第三方的支付寶,微信。其實 CAP 是由第三方來保證的,支付系統是一個對 CAP 要求極高的系統,C 是必須要保證的,AP 中 A 相對更重要,不能因為分割槽,導致所有人都不能支付

六、分散式 BASE 理論

​ CAP 不可能同時滿足,而分割槽容錯性(P)是對於分散式系統而言是必須的。如果系統能夠同時實現 CAP 是再好不過的了,所以出現了 BASE 理論。

(1) BASE 理論

通用定義

BASE 是Basically Available(基本可用)Soft state(軟狀態)Eventually consistent(最終一致性)三個短語的簡寫。

BASE 是對 CAP 中一致性和可用性權衡的結果,其來源於對大規模網際網路系統分散式實踐的總結,是基於 CAP 定理逐步演化而來的,其核心思想是即使無法做到強一致性,但每個應用都可以根據自身的業務特點,採用適當的方法來使系統達到最終一致性

兩個對衝理念:ACID 和 BASE

ACID是傳統資料庫常用的設計理念,追求強一致性模型。

BASE支援的是大型分散式系統,提出通過犧牲強一致性獲得高可用性

(2) Basically Available(基本可用)

實際上就是兩個妥協。

  • 對響應上時間的妥協:正常情況下,一個線上搜尋引擎需要在 0.5 秒之內返回給使用者相應的查詢結果,但由於出現故障(比如系統部分機房發生斷電或斷網故障),查詢結果的響應時間增加到了 1~2 秒。

  • 對功能損失的妥協:正常情況下,在一個電子商務網站(比如淘寶)上購物,消費者幾乎能夠順利地完成每一筆訂單。但在一些節日大促購物高峰的時候(比如雙十一、雙十二),由於消費者的購物行為激增,為了保護系統的穩定性(或者保證一致性),部分消費者可能會被引導到一個降級頁面,如下:

(3) Soft state(軟狀態)

  • 原子性(硬狀態) -> 要求多個節點的資料副本都是一致的,這是一種"硬狀態"

  • 軟狀態(弱狀態) -> 允許系統中的資料存在中間狀態,並認為該狀態不影響系統的整體可用性,即允許系統在多個不同節點的資料副本存在資料延遲。

(4) Eventually consistent(最終一致性)

上面說軟狀態,然後不可能一直是軟狀態,必須有個時間期限。在期限過後,應當保證所有副本保持資料一致性。從而達到資料的最終一致性。這個時間期限取決於網路延時,系統負載,資料複製方案設計等等因素。

稍微官方一點的說法就是:

系統能夠保證在沒有其他新的更新操作的情況下,資料最終一定能夠達到一致的狀態,因此所有客戶端對系統的資料訪問最終都能夠獲取到最新的值。

(5) BASE 總結

總的來說,BASE 理論面向的是大型高可用可擴充套件的分散式系統,和傳統事務的 ACID 是相反的,它完全不同於 ACID 的強一致性模型,而是通過犧牲強一致性來獲得可用性,並允許資料在一段時間是不一致的。

參考:

https://blog.csdn.net/weixin_44062339/article/details/99710968

https://blog.csdn.net/w372426096/article/details/80437198

https://www.solves.com.cn/it/cxkf/bk/2019-09-24/5229.html

https://www.jianshu.com/p/46b90dfc7c90

https://www.jianshu.com/p/9cb2a6fa4e0e

https://www.jianshu.com/p/68c7c16b3fbd


### 關於作者:

mail: danbing.at@gmail.com github: https://github.com/aceld 原創書籍 gitbook: http://legacy.gitbook.com/@aceld

創作不易, 共同學習進步, 歡迎關注作者, 回覆"zinx"有好禮

作者微信公眾號


文章推薦

開源軟體作品

(原創開源) Zinx-基於 Golang 輕量級伺服器併發框架-完整版 (附教程視訊)

(原創開源) Lars-基於 C++ 負載均衡遠端排程系統-完整版

精選文章

典藏版-Golang 排程器 GMP 原理與排程全分析

Golang 三色標記、混合寫屏障 GC 模式圖文全分析

最常用的除錯 golang 的 bug 以及效能問題的實踐方法?

Golang 中的 Defer 必掌握的 7 知識點

Golang 中的區域性變數 “何時棧?何時堆?”

使用 Golang 的 interface 介面設計原則

流?I/O 操作?阻塞?epoll?

深入淺出 Golang 的協程池設計

Go 語言構建微服務一站式解決方案


更多原創文章乾貨分享,請關注公眾號
  • 分散式從 ACID、CAP、BASE 的理論推進
  • 加微信實戰群請加微信(註明:實戰群):gocnio

相關文章