關於ACID,BASE和CAP定理的探究
阿新 • • 發佈:2020-08-23
## 前言
當我看到"**根據CAP理論,由於分散式系統必須保證分割槽容錯性,所以只能選擇AP原則或者CP原則**"這種結論時,我感到很疑惑:
- 什麼是分割槽容錯性?
- 為什麼分散式系統必須保證分割槽容錯性?
- 為什麼說Eureka這樣的分散式系統屬於AP原則,它明明沒有完全放棄一致性啊?
- 真的存在不用考慮資料一致性的系統嗎?AP和BASE有嚴格的區別嗎?
為了解決上面的疑惑,我查了很多資料,在下面的文章中,我會一一回答上面提出的問題。
## 事務和ACID
ACID是傳統關係型資料庫事務的四個特性,其中的四個字母分別代表以下單詞:
**A**tomicity, **C**onsistency, **I**solation, **D**urability
- **原子性(Atomicity)**: 指所有在事務中的操作要麼都成功,要麼都不成功,所有的操作都不可分割,沒有中間狀態。一旦某一步執行失敗,就會全部回滾到初始狀態。
- **一致性(Consistency)**: 在事務開始之前和事務結束以後,資料庫的完整性約束沒有被破壞。這表示寫入的資料必須完全符合所有的預設[約束](https://zh.wikipedia.org/wiki/資料完整性)、[觸發器](https://zh.wikipedia.org/wiki/觸發器_(資料庫))、[級聯回滾](https://zh.wikipedia.org/wiki/級聯回滾)等
- **隔離性(Isolation)**:資料庫允許多個併發事務同時對其資料進行讀寫和修改的能力,隔離性可以防止多個事務併發執行時由於交叉執行而導致資料的不一致。事務隔離分為不同級別,包括未提交讀(Read uncommitted)、提交讀(read committed)、可重複讀(repeatable read)和序列化(Serializable)。
- **永續性(Durability)**:事務處理結束後,對資料的修改就是永久的,即便系統故障也不會丟失
具有ACID特性的資料庫系統,可以保證在寫入或更新資料時,事務是正確可靠的。**ACID的目標是保證資料的正確性和一致性。**
## 從ACID到BASE
1997 年,Brewer提出BASE,他們的服務叢集在架構上放棄了關係型資料庫的 ACID 特性而使用BASE原則
BASE的四個字母分別代表以下單詞:
**B**asically **A**vailable, **S**oft State, **E**ventual Consistency
- **基本可用(Basically Available)**:分散式系統在出現不可預知故障的時候,允許損失部分可用性
- **軟狀態(Soft State)**:允許系統中的資料存在中間狀態,即不同節點上的資料不一致
- **最終一致性(Eventually Consistent)**:軟狀態不能一直持續,在一定期限過後,應當保證所有副本保持資料一致性,從而達到資料的最終一致性
BASE的思想是,為了提高系統的可用性,允許在某些時候,某個節點返回的不是最新的資料,但是在一段時間之後,系統中的資料最終會達到一致。這種做法可以在系統訪問高峰時犧牲一定的一致性,保證可用性,在高峰過後又恢復資料的一致性。
**可以看出,ACID和BASE兩種特性是對立的。**
ACID的特點是追求資料的一致性和正確性,但是這種嚴格的要求犧牲了系統的可用性(在資料的不同副本達到一致之前,這份資料都處於不可用的狀態)。而BASE則是在一定程度上容忍了資料出現暫時的不一致,從而提高了系統的可用性。**ACID追求一致性,而BASE追求可用性**。
這可能也是BASE命名的用意,因為ACID作為一個單詞時有”酸“的含義,而BASE作為一個單詞時有”鹼“的含義。
## 從BASE到CAP
### CAP猜想
2000 年,Brewer近一步分析比較了 ACID 和 BASE,引入”網路分割槽“,並提出CAP 猜想:
在分散式系統中,最多能同時滿足以下 3 個屬性中的 2 個:
C (Consistency), A (Availability), P (Tolerance to network Partitions)
### 網路分割槽(Network Partitions)
WIKI給出的定義:**網路分割槽指由於網路裝置的failure,造成網路分裂為多個獨立的組**
在分散式系統中,不同的節點之間通過網路互相連通。但是由於網路故障,可能會出現網路的"分裂"現象,**網路分成了不同的區域,區域內部之間可以的節點可以互相通訊,而區域之間無法互相通訊,這就是分割槽。**
![image-20200814232510689](http://img.hellochaos.cn/img/20200823152341.png)
顯然,網路分割槽是在分散式系統中常見的問題。
### CAP定理
2002 年,GilBert 和 Lynch,重新定義了 CAP 這 3 個屬性:
### C(Consistency):一致性
在完成寫請求後,不管客戶端訪問哪個節點,讀取到的都是同一份最新寫入的資料,這要求資料在不同節點上的副本保持一致。
### A(Availability):可用性
任何來自客戶端的請求,不管訪問哪個非故障節點,都能得到響應資料,強調服務可用,但是不保證返回的資料是正確的。
### P(Partition tolerance):分割槽容錯性
Gilbert和Lynch在論文中定義的分割槽容錯性:**網路允許丟失一個節點發給另一個節點的任意多的訊息**
我覺得從CAP猜想中更容易理解什麼是分割槽容錯性:
Brewer提出的分割槽容錯性是:*Tolerance to network Partitions*,直譯過來就是"對網路分割槽的容忍"。
**那麼分割槽容錯性含義就是:系統是否允許存在網路分割槽問題。**
如果一個系統要求在執行過程中不能發生網路分割槽,那麼這個系統就不具備分割槽容錯性。
可以想象,一個單機的MySQL資料庫不會發生網路分割槽,它肯定具有分割槽容錯性。
![image-20200821160946534](http://img.hellochaos.cn/img/20200823160542.png)
在明確了上面關於可用性,一致性和分割槽容錯性的定義之後,GilBert 和 Lynch證明了:
**在分散式系統中, CAP 這 3 個屬性不能同時達到,最多隻能同時滿足其中兩個,這就是著名的CAP 定理。**
需要注意的是**CAP定理的前提是分散式系統**,在單機應用中,不會發生網路分割槽,所以單機系統可以不具備分割槽容錯性,那麼此時CA可以同時達到,而對一個單機系統討論分割槽容錯性其實是毫無意義的。比如一個單機的資料庫,就可以兼顧可用性和一致性,並不存在網路分割槽問題。
![image-20200823140940551](http://img.hellochaos.cn/img/20200823152343.png)
## AP還是CP?
按照CAP定理的說法,我們只有三種選擇:CA,AP和CP
**那為什麼說“由於分散式系統必須保證分割槽容錯性,所以只能選擇AP架構或者CP架構”?**
答案是:"分散式系統中,網路分割槽無法避免”
前面提到,**在分散式系統中,不同的節點之間通過網路互相連通,網路分割槽問題根本無法避免,所以分散式系統一定要容忍發生網路分割槽,也就是一定要具有分割槽容錯性(Partition tolerance),所以分散式系統不能選擇CA。**(你無法要求在永遠不發生網路分割槽的環境下執行分散式系統)
但是,具有分割槽容錯性(Partition tolerance)並不意味著對於每一個客戶端的請求,都只能選擇A或者C中一個。**當系統中的節點可以互相正常通訊時,就可以同時保證可用性(Availability)和一致性(Consistency)。**
**那這是不是意味著分散式系統也能同時達到CAP呢?**
答案是否定的。
網路分割槽不會一直存在,但也無法避免。**一旦發生網路分割槽,分散式系統就只能在A和C之間做出選擇**:
- 保證一致性(Consistency),等待網路恢復時資料同步完成,才向客戶端提供服務,等待同步的過程服務不可用。
- 保證可用性(Availability),總是響應客戶端的請求,而不等待資料同步,服務可用但是不保證返回的資料是最新的。
可以看到,分散式系統在發生網路分割槽時,只能在A和C之間選擇其一。**換句話說,分散式系統不能永遠同時保證可用性和一致性**。所以,在設計分散式系統的時候,就要考慮清楚,如果發生了網路分割槽,要在A和C之間如何取捨,這決定了一個系統是AP原則還是CP原則:
- CP原則:如分散式資料庫、分散式鎖,服務註冊中心Zookeeper
- AP原則:如DNS,服務註冊中心Eureka
## 對比Eureka和Zookeeper在CAP中的選擇
CAP定理可以給我們一些啟示:
在設計分散式系統時,如果對資料正確性的要求很高,那麼選擇CP原則,犧牲可用性,保證一致性。如果對資料的一致性延遲不敏感,則可以選擇AP原則,實現高可用。
下面是一些常見分散式系統對AP和CP選擇:
| 名稱 | CAP |
| --------- | -------------------- |
| Eureka | AP |
| Consul | CP |
| Zookeeper | CP |
| Nacos | AP和CP兩種模式都支援 |
| Mongodb | CP |
**那麼為什麼說,Eureka是AP原則,而Zookeeper是CP原則?**![image-20200823140724626](http://img.hellochaos.cn/img/20200823152344.png)
**Eureka的各個節點是平等的**,每個節點都可以提供查詢和註冊服務。
在發生網路分割槽故障時,Eureka一開始會從註冊列表移除因長時間沒收到心跳的服務,但是當EurekaServer節點在短時間內丟失過多客戶端時,這個節點就會進入自我保護模式。此時Eureka不再從註冊列表移除因長時間沒收到心跳而應該過期的服務,並且仍然能夠接受新服務的註冊和查詢請求,但是不會被同步到其他節點,這使得**Eureka只要有一個節點存活就可以提供服務**。
![image-20200823160145788](http://img.hellochaos.cn/img/20200823160522.png)
**ZooKeeper採用Master/Slave 模式(主備模式)**,節點分為Leader,Observer和Follower角色。**Follower 和 Observer 都只能提供讀服務,只有Leader可以提供寫服務,Zookeeper叢集的寫入服務遵循"寫過半原則"**
在發生網路分割槽故障時,某些和Leader不在一個分割槽的節點無法和Leader取得聯絡,此時Zookeeper節點不會提供服務,而是進去Leader選舉流程,ZooKeeper在選舉期間註冊服務癱瘓,**雖然服務最終會恢復,但是選舉期間不可用的**。在選舉過程中會遵循“**過半原則**”,這可以保證資料只能通過叢集中唯一的Leader寫入,從而保證資料的一致性。
![image-20200823134809958](http://img.hellochaos.cn/img/20200823160529.png)
## ACID,BASE和CAP的聯絡
回過頭來看,CAP定理其實是對ACID和BASE兩種思想的抽象總結:
**CP原則延續了ACID的思想**,發生網路分割槽時會等待網路恢復時資料同步完成,才向客戶端提供服務,犧牲了系統的可用性。
**AP原則延續了BASE的思想**,總是響應客戶端的請求,而不等待資料同步,犧牲資料的一致性。
**那麼,AP系統完全放棄了資料一致性嗎?CP系統不具有可用性嗎?**
從前面討論中已經可以得到答案,只是在發生網路分割槽的時候,AP優先放棄資料一致性,而CP優先放棄系統可用性。在網路恢復正常之後,AP系統最終會恢復資料一致性,CP系統最終會恢復可用性。
## CP和ACID中一致性的區別
**ACID的一致性是內部一致性**,要求事務開始前和結束後,資料庫的完整性約束沒有被破壞。資料庫的完整性約束包括:
- 實體完整性
- 參照完整性
- 使用者定義的完整性
這裡主要是為了防止參照完整性和使用者定義的完整性被破壞,即[約束](https://zh.wikipedia.org/wiki/資料完整性)、[觸發器](https://zh.wikipedia.org/wiki/觸發器_(資料庫))、[級聯回滾](https://zh.wikipedia.org/wiki/級聯回滾)等。
**CAP的一致性是外部一致性**,要求在完成寫請求後,不管客戶端訪問哪個節點,讀取到的都是同一份最新寫入的資料,這要求資料在不同節點上的副本保持一致
## AP和BASE中可用性的區別
**AP和BASE看起來都選擇可用性而犧牲了一致性,那麼兩者有什麼區別呢?**
從發展歷史來看,BASE最早是Brewer在設計分散式系統時的經驗總結,而AP是後來提出的CAP定理的推論
從可用性的角度來看,AP是高可用,而BASE是基本可用,原因在於,AP系統對資料一致性的要求更低,而對可用性的要求更高,AP系統如Eureka甚至只要有一個節點存活就能提供服務。而BASE允許在高負載或者系統故障的情況下損失一定的可用性,比如在請求過多時,可以選擇非核心功能進行服務降級或熔斷,保證核心功能可用。
## BASE是誰提出的?
如果在百度或者谷歌上面搜尋BASE理論,很容易得到一個結果就是:"BASE理論是由eBay架構師Dan Pritchett提出的,是對CAP中一致性和可用性權衡的結果,其來源於對大規模網際網路系統分散式實踐的總結, 是基於CAP定理逐步演化而來的",但是我沒有找到有可靠的資料表明這是事實,百度百科和維基百科上面都沒有記載BASE理論的出處。
直到我找到這篇傳說中Dan Pritchett介紹BASE理論的文章:[Base: An Acid Alternative](https://queue.acm.org/detail.cfm?id=1394128)
從文章內容來看,Dan Pritchett並沒有提出BASE理論,只是在介紹了CAP定理之後,又介紹了和ACID相對的BASE思想,而且也提到了
> Following Brewer's conjecture, if BASE allows for availability in a partitioned database, then opportunities to relax consistency have to be identified.
因此,Dan Pritchett的文章更像是在介紹Brewer的BASE思想,而不是提出BASE理論。
本文對於BASE出處的介紹,來自於[NoSQL 資料庫不應該放棄 Consistency](https://www.infoq.cn/article/rhzs0KI2G*Y2r9PMdeNv)這篇文章,個人認為比"BASE理論是由eBay架構師Dan Pritchett提出的"的可信度更高。
## 總結
在瞭解了ACID,BASE和CAP定理提出的背景之後,站在提出者的角度思考,更容易理解這些理論的真正含義。至此,文章開頭提出的幾個問題也有了答案:
- 分割槽容錯性是在發生網路分割槽時系統仍然可以提供服務
- 分散式系統無法避免網路分割槽問題,所以必須保證分割槽容錯性
- AP和BASE都是在需要保證分割槽容錯性,又希望達到高可用時做出的妥協,並沒有完全放棄一致性,只是允許出現暫時的資料不一致
- 可以放棄強一致性,允許”軟狀態“,最後達到最終一致性,實現提高可用性的目的
此外,如果不存在網路分割槽問題,也就不需要考慮分割槽容錯性,此時A(Availability),C(Consistency)就可以同時實現。
## 參考資料
- [維基百科:ACID](https://zh.wikipedia.org/wiki/ACID)
- [維基百科:CAP定理]([https://zh.wikipedia.org/wiki/CAP%E5%AE%9A%E7%90%86](https://zh.wikipedia.org/wiki/CAP定理))
- [CAP定理與BASE理論](https://zhuanlan.zhihu.com/p/58538411)
- [資料完整性約束]([https://baike.baidu.com/item/%E6%95%B0%E6%8D%AE%E5%AE%8C%E6%95%B4%E6%80%A7%E7%BA%A6%E6%9D%9F](https://baike.baidu.com/item/資料完整性約束))
- [NoSQL 資料庫不應該放棄 Consistency](https://www.infoq.cn/article/rhzs0KI2G*Y2r9PMdeNv)
- [ACID 和 CAP 一致性的區別](https://blog.csdn.net/qq_36431213/article/details/85611721)
- [Base: An Acid Alternative](https://queue.acm.org/detail.cfm?id=1394128)
- [分散式-CAP理論和實現](https://juejin.im/post/6844903889225924616)
- [A clean way to implement database transaction in Golang](https://dev.to/techschoolguru/a-clean-way-to-implement-database-transaction-in-gol