這可能是我看過最通俗也是最深刻的CAP理論
本文的原作者是 Martin Kleppmann, 著有《Designing Data-Intensive Applications》一書,無論是這本書還是這篇文章,都能站在一個(gè)獨(dú)特的視角去闡釋那些可能被大多數(shù)人誤解的理念,讓讀者醍醐灌頂。
在此之前我就隱約對文中提到的一些 CAP 誤解嗤之以鼻,這篇文章讓我更加確信了之前零碎的認(rèn)知,不夸張地講,這應(yīng)該是我看過的最通俗也是最深刻的 CAP 科普文。
在 Jeff Hodges 精彩的博客文章給年輕人關(guān)于分布式系統(tǒng)的筆記中,他建議我們用 CAP 定理來評論系統(tǒng)。
很多人都聽取了這個(gè)建議,描述他們的系統(tǒng)為"CP" (有一致性但在網(wǎng)絡(luò)分區(qū)的時(shí)候不可用),“AP”(可用但是在網(wǎng)絡(luò)分區(qū)的時(shí)候不一致) 或者有時(shí)候 "CA" (說明"我還沒有讀過 Coda 的五年前的文章")。
我同意 Jeff 的所有觀點(diǎn),唯獨(dú)他關(guān)于 CAP 定理的觀點(diǎn),我必須表示不同意。
CAP 定理本身太簡單化而且被廣泛的誤解,以至于在描述系統(tǒng)上沒有太多用處。
因此我請求我們不要再引用 CAP 定理,不要再討論 CAP 定理。取而代之,我們應(yīng)該用更精確的術(shù)語來理解我們系統(tǒng)的權(quán)衡。
PS:沒錯(cuò),我意識到很諷刺的是我不希望別人再討論這個(gè)話題,但我卻正在分享一篇關(guān)于這個(gè)話題的博客文章。
但是至少這樣以后別人問我為什么不喜歡討論 CAP 定理的時(shí)候,我可以把這篇文章的鏈接給他。還有,抱歉這篇文章有些吐槽,但是至少這個(gè)吐槽有文獻(xiàn)引用。
CAP 用的是非常精確的定義
如果你想引用 CAP 作為一個(gè)定理(而不是一個(gè)模糊的,用來做數(shù)據(jù)庫市場營銷的概念),你需要用非常精確的定義。
數(shù)學(xué)要求精確,只有當(dāng)你的用詞和定理的證明中的定義是一樣的時(shí)候,這個(gè)證明才有意義。
CAP 的證明用的是非常具體的定義:
- 一致性(Consistency):在 CAP 中是可線性化的意思(linearizability)。而這個(gè)是非常特殊(而且非常強(qiáng))的一致性。
尤其是雖然 ACID 中的 C 也是一致性(Consistency),但是和這里的一致性沒有任何關(guān)系。我會在后面解釋可線性化是什么意思。
- 可用性(Availability):在 CAP 中是定義為"每一個(gè)請求(request)如果被一個(gè)工作中的[數(shù)據(jù)庫]節(jié)點(diǎn)收到,那一定要返回[非錯(cuò)誤]的結(jié)果"。
注意到,這里一部分節(jié)點(diǎn)可以處理這個(gè)請求是不充分的。任意一個(gè)工作中的節(jié)點(diǎn)都要可以處理這個(gè)請求。所以很多自稱"高度可用"的系統(tǒng)通常并沒有滿足這里的可用性的定義。
- 分區(qū)容錯(cuò)(Partition Tolerance):基本上就是說通信是在異步的網(wǎng)絡(luò)中。信息是可能延遲送達(dá)或者被丟失的。互聯(lián)網(wǎng)還有我們所有的數(shù)據(jù)中心都有這個(gè)屬性。所以我們在這件事上并沒有選擇。
還有就是注意到 CAP 并沒有描述任意一個(gè)老的系統(tǒng),而是一個(gè)非常特殊的系統(tǒng):
- CAP 系統(tǒng)的模型是一個(gè)只能讀寫單個(gè)數(shù)據(jù)的寄存器。這就是全部。CAP 沒有提到任何關(guān)于關(guān)系到多個(gè)事物(Object)的事務(wù)(Transaction)。
他們根本就不在這個(gè)定理的范圍之內(nèi),除非你可以把這些問題約化到一個(gè)單個(gè)寄存器的問題。
- CAP 定理只考慮了網(wǎng)絡(luò)分區(qū)這一種故障情況(比如節(jié)點(diǎn)們還在運(yùn)行,但是他們之間的網(wǎng)絡(luò)已經(jīng)不工作了)。這種故障絕對會發(fā)生,但是這不是唯一會出故障的地方。
節(jié)點(diǎn)可以整個(gè)崩潰(Crash)或者重啟,你可能沒有足夠的磁盤空間,你可能會遇到一個(gè)軟件故障(Bug),等等。
在建分布式系統(tǒng)的時(shí)候,你需要考慮到更多得多的問題。如果太關(guān)注 CAP 就容易導(dǎo)致忽略了其他重要的問題。
- 還有 CAP 根本沒有提到延遲(Latency)。而常常人們其實(shí)對關(guān)心延遲比可用性更多。
事實(shí)上,滿足 CAP 可用性的系統(tǒng)可以花任意長的時(shí)間來回復(fù)一個(gè)請求,而且同時(shí)保持可用性這個(gè)屬性。
我來冒險(xiǎn)說一句,我猜如果你的系統(tǒng)要花兩分鐘來加載一個(gè)頁面,你的用戶是不會稱它是“可用的”。
如果你的用詞是符合 CAP 證明中的精確定義的,那么它對你來說是適用的。但是如果你的一致性還有可用性是有其他意思的,那么你不能期待 CAP 對你還是適用的。
當(dāng)然,這并不意味著你通過重新定義一些詞匯就可以做到一些不可能的事情!這只是說你不能靠 CAP 來給你提供指導(dǎo)方向,而且你不能通過 CAP 來為你的觀點(diǎn)來辯解。
如果 CAP 定理不適用,那么這就意味著你必須自己來考慮取舍。你必須根據(jù)你自己對一致性還有可用性的定義來思考這些屬性,而且你能證明自己的定理就更好了。但是請不要稱它為 CAP 定理,因?yàn)檫@個(gè)名字已經(jīng)被用了。
可線性化
如果你對可線性化不是很熟悉(也就是 CAP 中的一致性),那么讓我來簡短地解釋一下。
正式的定義不是特別直觀,但是關(guān)鍵的思想用非正式的描述就是:
如果 B 操作在成功完成 A 操作之后,那么整個(gè)系統(tǒng)對 B 操作來說必須表現(xiàn)為 A 操作已經(jīng)完成了或者更新的狀態(tài)。
為了可以解釋的更清楚一些,讓我們來看一個(gè)例子。在這個(gè)例子中的系統(tǒng)并不是可線性化的。
看下面這個(gè)圖:
這張圖展示了 Alice 還有 Bob, 他們在同一個(gè)房間,都在用他們的手機(jī)查詢 2014 年世界杯的決賽結(jié)果。
就在最終結(jié)果剛發(fā)布之后,Alice 刷新了頁面,看到了宣布冠軍的消息,而且很興奮地告訴了 Bob。
Bob 馬上也重新加載了他手機(jī)上的頁面,但是他的請求被送到了一個(gè)數(shù)據(jù)庫的拷貝,還沒有拿到***的數(shù)據(jù),結(jié)果他的手機(jī)上顯示決賽還正在進(jìn)行。
如果 Alice 和 Bob 同時(shí)刷新,拿到了不一樣的結(jié)果,并不會太讓人意外。因?yàn)樗麄儾恢谰唧w服務(wù)器到底是先處理了他們中哪一個(gè)請求。
但是 Bob 知道他刷新頁面是在 Alice 告訴了他最終結(jié)果之后的。所以他預(yù)期他查詢的結(jié)果一定比 Alice 的更新。事實(shí)是,他卻拿到了舊的結(jié)果。這就違反了可線性化。
只有 Bob 通過另外一個(gè)溝通渠道從 Alice 那里知道了結(jié)果, Bob 才能知道他的請求一定在 Alice 之后。
如果 Bob 沒有從 Alice 那里聽到比賽已經(jīng)結(jié)束了,他就不會知道他看到的結(jié)果是舊的。
如果你在建一個(gè)數(shù)據(jù)庫,你不知道用戶們會有什么另外的溝通渠道。所以,如果你想提供可線性化(CAP 的一致性),你就需要讓你的數(shù)據(jù)庫看起來就好像只有一個(gè)拷貝,雖然實(shí)際上可能有多個(gè)備份在多個(gè)地方。
這是一個(gè)非常昂貴的屬性,因?yàn)樗竽阕龊芏鄥f(xié)調(diào)工作。甚至你電腦上的 CPU 都不提供本地內(nèi)存的可線性化訪問!
在現(xiàn)代的 CPU 上,你需要用 Memory Barrier 指令來達(dá)到可線性化訪問。甚至測試一個(gè)系統(tǒng)是不是可線性化的也是很困難的。
CAP 可用性
讓我們來簡短的討論一下為什么在網(wǎng)絡(luò)分區(qū)的情況下,我們要放棄可用性和一致性中的一個(gè)。
舉個(gè)例子,你的數(shù)據(jù)庫有兩個(gè)拷貝在兩個(gè)不同的數(shù)據(jù)中心。具體怎么做備份并不重要,可以是 Single-Master,或者多個(gè) Leader,或者基于 Quorum 的備份(Dynamo 使用的方式)。
要求是當(dāng)數(shù)據(jù)被寫到一個(gè)數(shù)據(jù)中心的時(shí)候,他也一定要被寫到另一個(gè)數(shù)據(jù)中心。
假設(shè) Client 只連接到其中一個(gè)數(shù)據(jù)中心,而且連接兩個(gè)數(shù)據(jù)中心的網(wǎng)絡(luò)故障了。
那么現(xiàn)在假設(shè)網(wǎng)絡(luò)中斷了,這就是我們所說的網(wǎng)絡(luò)分區(qū)的意思。接下來怎么樣呢?
顯然你有兩個(gè)選擇:
- 你的應(yīng)用還是被允許寫到數(shù)據(jù)庫,所以兩邊的數(shù)據(jù)庫還是完全可用的。但是一旦兩個(gè)數(shù)據(jù)庫之間的網(wǎng)絡(luò)中斷了,任何一個(gè)數(shù)據(jù)中心的寫操作就不會在另一個(gè)數(shù)據(jù)中心出現(xiàn)。
這就違反了可線性化(用之前的例子,Alice 可能鏈接到了一號數(shù)據(jù)中心,而 Bob 連接到了二號數(shù)據(jù)中心)。
- 如果你不想失去可線性化,你就必須保證你的讀寫操作都在同一個(gè)數(shù)據(jù)中心,你可能叫它 Leader。
另一個(gè)數(shù)據(jù)中心,因?yàn)榫W(wǎng)絡(luò)故障不能被更新,就必須停止接收讀寫操作,直到網(wǎng)絡(luò)恢復(fù),兩邊數(shù)據(jù)庫又同步了之后。
所以雖然非 Leader 的數(shù)據(jù)庫在正常運(yùn)行著,但是他卻不能處理請求,這就違反了 CAP 的可用性定義。
而這個(gè),其實(shí)就是 CAP 定理的證明。這就是全部了。這里的例子用到了兩個(gè)數(shù)據(jù)中心,但是對于一個(gè)數(shù)據(jù)中心內(nèi)的網(wǎng)絡(luò)故障也是同樣適用的。我只是覺得用兩個(gè)數(shù)據(jù)中心這樣更容易考慮這個(gè)問題。
注意到上面第二點(diǎn),就算它違反了 CAP 的可用性,但我們還是在成功地處理著請求。
所以當(dāng)一個(gè)系統(tǒng)選擇了可線性化(也就是說不是 CAP 可用的),這并不一定意味著網(wǎng)絡(luò)分區(qū)一定會造成應(yīng)用停運(yùn)。
如果你可以把用戶的流量轉(zhuǎn)移到 Leader 數(shù)據(jù)庫,那么用戶根本就不會注意到任何問題。
實(shí)際應(yīng)用中的可用性和 CAP 可用性并不相同。你應(yīng)用的可用性多數(shù)是通過 SLA 來衡量的(比如 99.9% 的正確的請求一定要在一秒鐘之內(nèi)返回成功)。
但是一個(gè)系統(tǒng)無論是否滿足 CAP 可用性其實(shí)都可以滿足這樣的 SLA。實(shí)際操作中,跨多個(gè)數(shù)據(jù)中心的系統(tǒng)經(jīng)常是通過異步備份(Asynchronous Replication)的,所以不是可線性化的。
但是做出這個(gè)選擇的原因經(jīng)常是因?yàn)檫h(yuǎn)距離網(wǎng)絡(luò)的延遲,而不是僅僅為了處理數(shù)據(jù)中心的網(wǎng)絡(luò)故障。
很多系統(tǒng)既不是可線性化的也不是 CAP 可用的
在 CAP 對可用性還有一致性嚴(yán)格的定義下,系統(tǒng)們表現(xiàn)怎么樣?
拿任意一個(gè) Single Master 的有備份的數(shù)據(jù)庫作為一個(gè)例子。這也是標(biāo)準(zhǔn)的數(shù)據(jù)庫設(shè)置。
在這種情況下,如果用戶不能訪問 Leader,就不能寫到數(shù)據(jù)庫。雖然他還能從 Follower 那里讀到數(shù)據(jù),但是他不能寫任何數(shù)據(jù)就說明它不是 CAP 可用的。更不要說這種設(shè)置還常常聲稱自己是“高可用的(High Availablity)”。
如果以上這種設(shè)置不是 CAP 可用的,那是不是就是說他滿足 CP(一致)?
等一下,如果你是從 Follower 那里讀到的數(shù)據(jù),因?yàn)閭浞菔钱惒降?,所以你可能讀到舊的數(shù)據(jù)。所以你的讀操作不是可線性化的,所以不滿足 CAP 中的一致性。
而且支持 Snapshot Isolation/MVCC 的數(shù)據(jù)庫是故意做成不可線性化的。否則會降低數(shù)據(jù)庫的并發(fā)性。
比如 PostgreSQL 的 SSI 提供的是可串行化而不是可線性化,Oracle 兩者都不支持。僅僅因?yàn)閿?shù)據(jù)庫標(biāo)榜自己是 ACID 并不意味著它就滿足 CAP 中的一致性。
所以這些系統(tǒng)既不是 CAP 一致的,也不是 CAP 可用的。他們既不是 CP 也不是 AP,他們只是 P,不管這是什么意思。(是的,“三選二”也允許你只從三個(gè)中選一個(gè),甚至一個(gè)都不選!)
那 NoSQL 怎么樣的?拿 MongoDB 作為一個(gè)例子:每一個(gè) Shard 都只有一個(gè) Leader(至少只要他不在 split-brain 的模式下,它應(yīng)該是這樣的),根據(jù)以上的論證,那就說明他不是 CAP 可用的。
而且 Kyle 最近發(fā)現(xiàn),設(shè)置了***的一致性,他還是允許非一致性的讀操作,所以它也不是 CAP 一致的。
那像 Riak,Cassandra 還有 Voldemort 這些聲稱是 AP 的高可用的 Dynamo 的繼承者們又怎么樣呢?
這取決于你的設(shè)置。如果你接受讀寫只訪問一個(gè)拷貝(R=W=1),那么這確實(shí)是 CAP 可用的。
但是如果你要求 Quorum 讀寫(R+W>N),而且你有網(wǎng)絡(luò)分區(qū),那么那些被分在少部分節(jié)點(diǎn)的用戶就不能達(dá)到 Quorum。
所以 Quorum 操作不是 CAP 可用的(至少暫時(shí)是不可用的,直到你在少部分的分區(qū)內(nèi)加入了更多的節(jié)點(diǎn))。
你有時(shí)候會看到人們聲稱 Quorum 讀寫可以保證可線性化,但是我覺得依賴這樣的聲明是不明智的。
因?yàn)樵谝恍?fù)雜的情況下,Read Repair 操作和 Sloppy Quorum 同時(shí)發(fā)生,就有可能會重寫已經(jīng)被刪除了的數(shù)據(jù)。
或者當(dāng)備份數(shù)(Replicas)已經(jīng)低于原來的 W 值(違反了 Quorum 的條件),或者當(dāng)備份數(shù)被加到了高于原來的 N 值(還是違反了 Quorum 的條件),這些都可以導(dǎo)致不可線性化的訪問結(jié)果。
這些都不是差的系統(tǒng):他們在實(shí)際運(yùn)用中都很成功。但是目前為止,我們還是不能嚴(yán)格把他們分類為 AP 或者 CP,要么是因?yàn)槿Q于具體的設(shè)定,或者是因?yàn)檫@個(gè)系統(tǒng)一致性和可用性都不滿足。
案例分析:ZooKeeper
那 ZooKeeper 又怎么樣呢?他用了 Consensus 算法,所以人們一般認(rèn)為他是很清楚的選擇了一致性而放棄了可用性(也就是 CP 系統(tǒng))。
但是如果你閱讀 ZooKeeper 的文檔,他們很清楚的說了 ZooKeeper 的默認(rèn)設(shè)置不提供可線性化的讀操作。
每一個(gè)連接到一個(gè)服務(wù)器的客戶端,當(dāng)你要讀的時(shí)候,即使別的節(jié)點(diǎn)有更新的數(shù)據(jù),你只能看到那個(gè)服務(wù)器本地的數(shù)據(jù)。
這樣讀操作就比需要收集 Quorum 或者訪問 Leader 要更快。但這也說明 ZooKeeper 默認(rèn)不滿足 CAP 的一致性定義。
做可線性化的讀操作在 ZooKeeper 中是支持的。你需要在讀操作之前發(fā)一個(gè) Sync 命令。
但這不是默認(rèn)的設(shè)置,因?yàn)檫@樣讀操作會更慢。人們有時(shí)候會用 Sync 命令,但一般不會是所有的讀操作都用。
那 ZooKeeper 的可用性呢?他要求達(dá)到大多數(shù) Quorum,來達(dá)到共識,才能處理一個(gè)寫操作。
如果你有網(wǎng)絡(luò)分區(qū),一邊有大多數(shù)節(jié)點(diǎn),一邊有少部分節(jié)點(diǎn)。那么擁有大多數(shù)節(jié)點(diǎn)的分區(qū)還可以繼續(xù)工作,但是少部分節(jié)點(diǎn)的分區(qū)就算節(jié)點(diǎn)們都正常工作著,還是不能處理寫操作。
所以 ZooKeeper 的寫操作在網(wǎng)絡(luò)分區(qū)的情況下,不滿足 CAP 的可用性(即使擁有大多數(shù)節(jié)點(diǎn)的分區(qū)還是可以處理寫操作的)。
更有意思的是,ZooKeeper 3.4.0 還加入了一個(gè)只讀的模式。在這個(gè)模式下,少部分節(jié)點(diǎn)的分區(qū)還可以繼續(xù)處理讀操作,不需要 Quorum!
這個(gè)讀操作是滿足 CAP 可用性的。所以 ZooKeeper 默認(rèn)設(shè)置既不是一致的(CP)也不是可用的(AP),只是"P"。
但是你有選擇通過用 Sync 命令來讓它成為 CP。并且在正確的設(shè)置下,讀操作(不包括寫)其實(shí)是 CAP 可用的。
這讓人不是很舒服。如果就因?yàn)?ZooKeeper 的默認(rèn)設(shè)置不是可線性化的就稱他為不一致,那就歪曲了他的功能。
他其實(shí)可以提供非常強(qiáng)的一致性!他支持 Atomic Broadcast(這個(gè)可以約化為共識問題)以及每個(gè) Session 的 Causal Consistency。
這比 read your writes,monotonic reads 還有 consistent prefix reads 在一起都要強(qiáng)。
他的文檔上說 ZooKeeper 提供可串行化的一致性,但這其實(shí)是過于謙虛了,因?yàn)樗鋵?shí)可以提供更強(qiáng)的一致性。
根據(jù) ZooKeeper 的例子,你就會發(fā)現(xiàn)就算這系統(tǒng)在網(wǎng)絡(luò)分區(qū)的時(shí)候既不是 CP 也不是 AP(甚至在默認(rèn)設(shè)置下,就算沒有網(wǎng)絡(luò)分區(qū),也不是可線性化的),但他還是很合理的。
我猜 ZK 在 Abadi 的 PACELC 的框架下是 PC/EL,但我不覺得這比 CAP 更有啟發(fā)性。
CP/AP:一個(gè)偽二分法
事實(shí)上我們都沒有成功地把一個(gè)數(shù)據(jù)庫無歧義地分類為 AP 或者 CP。這應(yīng)該告訴我們 CP/AP 根本就不是合適的用來描述系統(tǒng)的標(biāo)簽。
我相信我們應(yīng)該不要再把數(shù)據(jù)庫歸類為 AP 或者 CP 了,因?yàn)椋?/p>
在同一個(gè)軟件內(nèi),你可能有多個(gè)一致性屬性的選擇。
- 很多系統(tǒng)在 CAP 的定義下,既不是一致也不可用。然而我從來沒有聽到別人稱這些系統(tǒng)為"P",可能是因?yàn)檫@樣不太好看。但這并不差,他很可能是完全合理的設(shè)計(jì),他只是不在 CP/AP 這兩個(gè)分類中。
- 雖然大部分軟件都不在 CP/AP 這兩類中,但人們還是強(qiáng)行把軟件分為這兩類。這就導(dǎo)致了,為了適用,不可避免地改變對“一致性”或者“可用性”的定義。
不幸的是,如果用詞的定義改變了,CAP 定理自己也不適用了,那 CP/AP 區(qū)分也就完全沒有意義了。
- 把系統(tǒng)分為這兩類,導(dǎo)致了很多細(xì)節(jié)被忽略。在考慮分布式系統(tǒng)設(shè)計(jì)的時(shí)候,會有很多關(guān)于容錯(cuò),延遲,簡單模型,運(yùn)行成本,等等的考慮。把那么多細(xì)節(jié)編碼到一個(gè)比特的信息,顯然是不可能的。
比如說雖然 ZooKeeper 有一個(gè) AP 的只讀模式,但這個(gè)模式也提供對所有寫操作的 total ordering。
這比 Riak 或者 Cassandra 這些 AP 系統(tǒng)提供的保障要強(qiáng)得多。所以簡單地把他們都?xì)w為 AP 一個(gè)類別就顯得很不合理。
- 甚至 Eric Brewer 承認(rèn) CAP 是一個(gè)容易誤導(dǎo)人的而且過于簡化的模型。在 2000 年,CAP 的意義在于讓大家開始討論關(guān)于分布式系統(tǒng)的取舍。
他在這方面做得很好,但是他不是用來作為一個(gè)正式的突破性的結(jié)果,也不是一個(gè)嚴(yán)格的數(shù)據(jù)系統(tǒng)的分類方式。
15 年之后,我們已經(jīng)有了多得多的有不一樣一致性和容錯(cuò)模型的系統(tǒng)。CAP 已經(jīng)完成了他自己的使命,現(xiàn)在是時(shí)候不要在糾結(jié)了。
學(xué)會獨(dú)立思考
如果用 CP 和 AP 來描述和評論系統(tǒng)是不合適的,那么我們應(yīng)該用什么呢?我不認(rèn)為有一個(gè)唯一的答案。
很多人花了很多心思考慮這些問題,也提出了術(shù)語和模型來幫助我們理解這些問題。
想要學(xué)習(xí)這些思想,你就需要更深入自己閱讀文獻(xiàn):
- 一個(gè)很好的起點(diǎn)就是 Doug Terry 的論文。其中他用棒球來解釋了各種不一樣的最終一致性??勺x性很強(qiáng),而且就算對像我這樣不是美國人而且完全不懂棒球也解釋的很清晰。
- 如果你對 Transaction 的 Isolation 模型有興趣(這和分布式系統(tǒng)的一致性不一樣,但是相關(guān)),我的小項(xiàng)目 Hermitage 你可以看一下。
- 這篇論文討論了分布式系統(tǒng)的一致性和 Transaction 的 Isolation 以及可用性之間的關(guān)系。(這篇論文也描述了不同一致性之間的分級。Kyle Kingsbury 很喜歡給別人講這個(gè)。)
- 當(dāng)你讀到過這些了以后,你應(yīng)該已經(jīng)準(zhǔn)備好深入閱讀論文。我在這篇文章中加入了很多對文獻(xiàn)的引用。去看一下,很多專家已經(jīng)幫你把很多問題都已經(jīng)解決了。
- 作為***的手段,如果你不想讀論文原文,我建議你看一下我的書。這本書用通俗易懂的方式總結(jié)了大多數(shù)重要的思想。
- 如果你想學(xué)更多關(guān)于怎么正確使用 ZooKeeper,F(xiàn)lavio Junqueira 還有 Benjamin Reed 的書是非常不錯(cuò)的。
不管你選擇哪一種學(xué)習(xí)方式,我都鼓勵(lì)你保持好奇心和耐心,因?yàn)檫@不是容易的學(xué)科。
但是這是有回報(bào)的,因?yàn)槟銓W(xué)會如果考慮取舍,進(jìn)而搞清楚什么樣的架構(gòu)對于你的應(yīng)用是最合適的。
但是不管你做什么,請不要再說 CP 還有 AP 了,因?yàn)楦静缓侠怼?/p>
***,謝謝 Kyle Kingsbury 還有 Camille Fournier 對于這篇文章初稿的評論。當(dāng)然,所有的錯(cuò)誤還有不受歡迎的觀點(diǎn)都是我本人的。