當年,我的架構師之路差點完蛋,幸虧了它

四猿外發表於2021-01-04

這次和大家講講分散式事務的 BASE 理論,保證通俗易懂。為了閱讀順暢,開始之前先請大家記住幾個名詞:

BASE——Basically Available(基本可用),Soft state(軟狀態),Eventually consistent(最終一致性)
2PC——兩階段提交

不用懂,先記住就好了。你負責記住,我負責讓你懂。

正文開始:

深夜,我嗒嗒嗒的敲著鍵盤,我在螢幕上敲下了這麼一段話:

“2008 年 Dan Pritchett 提出一個與兩階段提交截然不同的分散式事務理論: BASE(Basically Available,Soft state,Eventually consistent)理論。BASE 理論打破了傳統解決分散式事務的思維,放棄 ACID 特性以換取系統的可用性,BASE 理論強調基本可用、軟狀態、最終一致,而不像 ACID 堅持強一致性。BASE 理論是一種處理分散式事務的思想,沒有具體的操作步驟,要理解 BASE 理論需要結合具體的例子。”

敲完這段話以後,我頓了下,完全停下了打字。思考了很久,我決定把準備了四五天的,各種為了講清楚 BASE 理論的應用例項全部刪掉。

因為我很想談談自身的一些經歷。也許,那些折騰和熬人的經歷能更清楚的告訴大家,為什麼會有 BASE 理論這套東西出來。

一、

前些年,網際網路行業裡對架構師這個崗位的標準還不是很清晰。所以,很多架構師的工作往往就是一些技術被公司認可的資深工程師負責。

彼時,正巧我也是這類人員之一,故也得到了一個從零開始架設一套廣告投放平臺的機會。

我很喜歡鑽研技術,對這種機會自然很看重。

那時候,架構並無如今這麼複雜,一開始就是前面搞幾個 Web 應用,後面共享個資料庫。大致像這樣:

當然,上面的架構其實做了很多簡化,省略了很多細節。比如,為了提高效能做的快取,為了提高吞吐做的負載均衡統統沒有在上圖給出。因為這些和本章話題無關,暫時我們們就忽略這些東西,只看核心部分。

這套架構初期執行還是沒什麼問題的,再加上一些快取機制,初期一些效能問題都通過調整快取提升快取的碰撞率應付了過去。

可是,隨著廣告投放量的增大,廣告的訪問量也在暴漲。這些暴漲的訪問量引發了效能問題。當時,由於前端有負載均衡,應用層倒是沒出現什麼問題……

問題出在後面的資料庫上

二、

這套架構資料庫用的是 MySQL,本身也只有一臺主庫在對外服務,另外一臺備庫採用了 MySQL 自己的全同步機制做實時備份。

當廣告訪問量暴漲的時候,因為業務需要,很多資料需要在資料庫中做實時插入,這就導致了大量的磁碟 IO 產生。這些大量的磁碟 IO 造成了資料庫本身效能的急劇下降。

悲催的是,整套廣告平臺的所有功能又都是共享一個資料庫的,所以隨著資料庫本身的效能下降,平臺的所有功能都受到了影響。

由於問題主要在於大量廣告流量的寫入,所以,靠讀寫分離的方案去緩解問題這條路就走不通了。

只好先升級硬體了。在經過了幾輪硬體升級和資料庫調優之後,單資料庫再也無法支撐不斷上漲的流量了。沒辦法,要考慮搞資料庫切分了。

那時候,我個人是很恐懼資料庫切分的。

原因不僅僅在於需要在應用層多寫很多複雜的邏輯,其根本原因是當時流行的 2PC(兩階段提交)方案,這個方案本身能保證在資料庫切分的情況下,原來的事務依然保留著自身的 ACID 性質。即:

  1. Atomicity(原子性),不管事務裡執行多少命令,對外它們都是一體的,要麼都執行,要麼都不執行。
  2. Consistency(一致性),正因為事務裡要麼做要麼都不做,所以資料庫的狀態變化只能由事務變更後,才會叫一致性狀態。
  3. Isolation(隔離性),事務裡做的事兒事務外面誰也看不到,就跟個盒子把資料罩起來一樣,到底中間怎麼變化的,事務外面的觀察不到。
  4. Durability(永續性),事務確認成功了,那這狀態就永久不變了。

但也正因為這 4 個特性,2PC 才讓我顧慮重重。

顧慮1:首先,資料庫拆分了,那麼根據事務的原子性,事務自身必須是一體的,那麼事務涉及到的不同的資料庫就必須都訪問一遍,而這本身就意味著很高的通訊成本。

再加上,為了保持一致性,事務失敗後,還必須恢復各個資料庫原來的狀態,這就必須讓已經成功執行過本地事務的資料庫全部回滾。

而稍微懂點資料庫的人都知道,這個成本有多大。

更可怕的是,本身事務的隔離性還可能加上鎖。一旦一個熱點資料區域被大量訪問,最差情況就可能出現序列訪問。而這對此套平臺,包括我自己都將是個悲劇。

顧慮2:資料庫的拆分會造成整個平臺的可用性下降。

假設我現在有一臺資料庫,它的可用性是 99.9%。如果因為分庫,資料庫從一臺變成兩臺,那麼平臺的可用性就會變成:

平臺的可用性 = 99.9% * 99.9% = 99.8%

從 99.9% 變成了 99.8%,這意味著可用性下降了 0.1%,每個月的不可用時間會增加 43 分鐘之多。

一邊是硬體升級已經到頂,單機資料庫也優化到了極限,再不做資料庫拆分,平臺可能隨時癱瘓。一邊是沒有好的策略,可能拆分資料庫後,每個月都有當機的風險,同時效能也可能會出現劇烈的下降。

我被逼入了死角。

三、

這種痛苦的糾結折磨了我大概一週,直到我看到了 CAP 定理。當 CAP 定理說分散式系統在分割槽容錯的時候,只能一致性和可用性二選一時,我高興的蹦了起來。

原來,可用性和一致性是不能兼得的。

為何我會那麼高興?因為逼我入死角的可不僅是技術上的問題了,我還承受著來自於業務方和領導的壓力。每天一上班,我就需要面對業務各方的抱怨,以及領導一輪又一輪的催促。

有了 CAP 定理的支援,我知道我最終是要面臨選擇的。既然在這個世界上做分散式架構的所有人都要面臨選擇,那我又怎麼可能獨善其身呢?

在對單機資料庫引發的各種問題做了一次徹底的各種歸因以後,我下了決心:

一定要搞定拆分資料庫並給出良好方案。

只是,2PC 這個攔路虎,它成為了我的大敵。通過 CAP 定理,我非常肯定,只要我選了 2PC 方案,可用性就一定會出現嚴重的問題,這個方案也肯定不可能拿出來丟人現眼的。

我唯一的方向就是去犧牲一些一致性,往可用性方向走。可是,怎麼走呢?

也許是老天眷顧,也許是大家都承受著和我一樣夜不能寐的壓力,很快,BASE 理論在國內傳開了。

BASE 理論讓我知道了,這個世上能排到前幾名的技術大公司也一樣會出問題,也一樣會對這些問題進行妥協。而且 BASE 理論的思想讓我的思路一下子就開啟了,苦思而不得的問題開始有了頭緒。

我要開始著手製定技術方案了。

四、

BASE 思想中的 BA(Basically Available)基本可用,是鼓勵通過預先的架構設計或者前期規劃,儘量在分散式的系統中,把以前可能影響全平臺的嚴重問題,變成只會影響平臺中的一部分資料或者功能的非嚴重問題。

有了這個思想之後,我就對廣告平臺中的很多重要的資料表進行了拆分,並將這些表的資料分散到了不同的資料庫中。

比如,有個廣告流量詳情表,每當使用者點選廣告或者廣告展示出來的時候,為了保證不丟失,這些資料都是實時插入到這個表裡的。

我對這張表是怎麼切分的呢?

當有人點選廣告了,他的點選記錄會被傳到我的應用層,然後我會在應用層根據廣告 ID 做雜湊,再根據雜湊結果的不同,分別存到不同的資料庫中去。

假如這三個資料庫中的一個出現了問題,則只會有三分之一的資料受到影響。這就實現了 BASE 理論中的 BA——基本可用了。基本可用其實也真的就是表達的這麼一回事:

通過一些架構設計,即使平臺中某部分元件出現了問題,也不會導致整個平臺不可用。

好了,既然採取了資料庫拆分的策略,又根據 BASE 理論中的 BA 思想拆分了一些重要的表,那麼,到了現在,可能也無從後悔,只能繼續沿著 BASE 這條路,一條路走到黑了。

五、

接下來,需要著手解決效能問題了。2PC 方案……算了……它瘋狂的一致性性格會要了我的狗命的。

那麼極端點,我們不搞事務可不可以呢?

還用前面說的那套廣告平臺舉例。

當時,從業務上,要求廣告的訪問資料都要保證及時入庫不能丟,因為丟了就可能造成計費的損失,而這些損失全是錢。所以,每當使用者點選廣告或者廣告展示出來的時候,為了保證不丟失,這些資料都是實時入庫的。

又根據業務需求,當廣告流量入庫時,還需要往廣告預算表和媒體流水錶裡同時根據這筆流量進行記賬,以供後續財務計算。

如果完全不考慮事務,則拆分庫後,操作可能會是這個樣子。

這三個操作可能會並行發往不同的資料庫執行。由於三個操作之間沒有事務的約束,所以,一個操作出問題了,另外的操作並不會受到影響。

而這卻也引發了另外一個問題,資料狀態不一致。

如果在上面的業務中,插入廣告流量表的操作失敗了,但其餘兩張表插入成功了,業務就會面臨一個很尷尬的情況:他們算出的財務報表沒有依據。財務流水中找不到產生了這筆流水的依據。

而這種不一致的狀態由於已經被持久化到了資料庫中,就會導致這種不一致的狀態永久存在了資料庫中。這業務能接受嗎?但凡有點職業精神的程式設計師能接受嗎?

要有折中的辦法!!!

六、

現在再回過頭來看看 2PC 的問題。假設 2PC 的實現是一步步執行的(當然,不管是一步一步還是非同步併發,他們總是要確保大家要麼一起成功要麼一起失敗的。

所以,即使併發操作,也不會節省多少效能,因為短板在執行最慢的那條語句上。如果執行我們上面的事務需要幾步呢?

假如現在要執行事務 A:

  1. 協調器發出事務 A 中的第一條語句 Insert into 流量表
  2. 協調器等待結果
  3. 協調器發出事務 A 中的第二條語句 Insert into 預算表
  4. 協調器等待結果
  5. 協調器發出事務 A 中的第三條語句 Insert into 流水錶
  6. 協調器等待結果

如果中間有失敗的,協調器還需要做額外的操作:

  1. 協調器告訴事務 A 中第一條語句做回滾操作
  2. 協調器等待結果
  3. 協調器告訴事務 A 中第二條語句做回滾操作
  4. 協調器等待結果
  5. 協調器告訴事務 A 中第三條語句做回滾操作
  6. 協調器等待結果

“天哪,這麼多步操作啊!!!”

這簡直是讓人窒息的操作步驟了。如果有一種方法既能節省步驟又能節省事務執行時間該有多好啊。

嗯……我只能說當時的自己實在是長得醜卻想的美。

世上尚不存在這種方法的。但是,世上還存在另外的解決此類事情的方式:

非同步處理,時間分攤

我們分析下關於插入廣告流量這塊兒的業務。你會發現一個神奇的現象,即廣告流量表中的資料才是核心,而預算表和流水錶統統都是廣告流量表中資料的一種快取而已。

如果,嗯,我是說如果有這麼一種辦法,即我們先把廣告流量資料插入資料庫,成功以後,再把以廣告流量資料作為根基的附屬操作(這裡是插入預算表和流水錶)放到一個地方持久化。然後,我們再從那個存放附屬操作的地方把操作資訊取出來,專門對這些操作資訊進行處理。

而這種處理方式可能會非常靈活,要麼可以對這些操作資訊進行批量處理,要麼可以對他們非同步的在後臺處理。處理這些操作資訊成功以後,再把以前持久化好的操作資訊給刪除。

整個方法實施下來,相當於把應該在 A 時刻在前臺阻塞著花 3 秒處理業務的操作,變成了在 A 時刻前臺花 1 秒,然後在 B 時刻後臺花 2 秒處理業務的操作,這不也可以變相的達到我們想節省步驟和事務執行時間的目標了嗎?

這真的是一個好的思路啊,還記得當時的自己想到這個思路的時候,忍不住在內心大喊了起來:“那個存附屬操作資訊的地方就是 MQ 啊。用 MQ,MQ 就能做這件事情。”

那麼就一起來看下 MQ 是如何幫我解決這個大難題的吧,針對上面的廣告流量詳情的業務,我們用了 MQ 之後會有如下的步驟:

  1. 執行 Insert into 流量表語句
  2. 等待結果
  3. 發訊息到 MQ 裡,內容為 Insert into 預算表
  4. 等待 MQ 持久化成功
  5. 發訊息到 MQ 裡,內容為 Insert into 流水錶
  6. 等待 MQ 持久化成功

如果發給 MQ 訊息失敗:

  1. 可以降級寫到本地日誌中

OK,那麼這改進後的方法是怎麼提升效能的呢?

  • 首先,我們發給 MQ 的訊息可以批量傳送;
  • 其次,發給 MQ 並持久化訊息要比資料庫執行一次事務快了一個數量級;
  • 最後,失敗後,回滾操作成本降低了不止一個數量級。

這個方法本質上,在應用層其實就執行了一條語句而已,剩下的完全可以根據業務需求的不同,選擇處理 MQ 中的訊息的方式。比如,處理訊息既可以非同步慢慢處理,也可以推遲一段時間後處理,更可以凌晨定時處理。

可以看到,使用 MQ 方案後,對廣告流量這個業務需求而言,其實,出現了一箇中間狀態:廣告流量表有資料,但是以這條資料為基準的預算表和流水錶暫時還沒有資料。

中間這個狀態此時是不滿足業務需求的。而這種狀態,在 BASE 理論中就被稱為:

軟狀態(Soft state)

至於廣告流量表當時沒有及時插入到預算表和流水錶中的資料呢,它們最終也將會隨著後續對 MQ 訊息的處理而被補充完整的。

而對於這種當時不符合業務需求的軟狀態,通過一些後續內部的自動化操作把資料狀態補充完整從而最終滿足業務需求的情況,在 BASE 理論中就被稱為了:

最終一致性(Eventually consistent)

由此,我通過不斷利用 BASE 理論中的軟狀態和最終一致性的思路,最終補上了平臺資料庫切分需要的最後一塊拼圖——平臺效能大提升

我蛻變了!!!

最後

以上就是 BASE 理論是如何把我救於水火的經歷,不知道你從此又會對 BASE 理論理解了多少呢?

再重複一次,BASE 理論本質上只是一種架構思想,它告訴人們世界上還存在著這麼一些事情:

  1. 能通過巧妙地設計,通過區域性輕微的損失減少全域性嚴重的損失;

  2. 能通過一些解耦、非同步、推遲執行、批量執行等技巧,構造出一種中間狀態,從而提高系統的整體效能;

  3. 平臺是為業務服務的,業務的核心是資料狀態,而資料狀態無論中間變成什麼樣,最終還要恢復到它應該處於的正確狀態。

這就是BASE理論的基本可用、軟狀態和最終一致性了。

為了寫這篇,又熬了好幾個夜,如果覺得寫的不賴,願意讓更多人看到,期待你的點贊和轉發。

(完)


我準備了一些純手打的高質量PDF:

深入淺出Java多執行緒、HTTP超全彙總、Java基礎核心總結、程式設計師必知的硬核知識大全、簡歷面試談薪的超全乾貨。

別看數量不多,但篇篇都是乾貨,看完的都說很肝。

領取方式:掃碼關注後,在公眾號後臺回覆:666

相關文章