當年,個人架構師之路差點完蛋,幸好了它

此次和你們講講分佈式事務的 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 理論這套東西出來。異步

 

1、

前些年,互聯網行業裏對架構師這個崗位的標準還不是很清晰。因此,不少架構師的工做每每就是一些技術被公司承認的資深工程師負責。分佈式

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

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

那時候,架構並沒有現在這麼複雜,一開始就是前面搞幾個 Web 應用,後面共享個數據庫。大體像這樣:

固然,上面的架構其實作了不少簡化,省略了不少細節。好比,爲了提升性能作的緩存,爲了提升吞吐作的負載均衡通通沒有在上圖給出。由於這些和本章話題無關,暫時我們就忽略這些東西,只看核心部分。

這套架構初期運行仍是沒什麼問題的,再加上一些緩存機制,初期一些性能問題都經過調整緩存提高緩存的碰撞率應付了過去。

但是,隨着廣告投放量的增大,廣告的訪問量也在暴漲。這些暴漲的訪問量引起了性能問題。當時,因爲前端有負載均衡,應用層卻是沒出現什麼問題……

問題出在後面的數據庫上

 

2、

這套架構數據庫用的是 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 分鐘之多。

一邊是硬件升級已經到頂,單機數據庫也優化到了極限,再不作數據庫拆分,平臺可能隨時癱瘓。一邊是沒有好的策略,可能拆分數據庫後,每月都有宕機的風險,同時性能也可能會出現劇烈的降低。

我被逼入了死角。

 

3、

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

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

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

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

在對單機數據庫引起的各類問題作了一次完全的各類歸因之後,我下了決心:

必定要搞定拆分數據庫並給出良好方案。

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

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

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

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

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

 

4、

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

有了這個思想以後,我就對廣告平臺中的不少重要的數據表進行了拆分,並將這些表的數據分散到了不一樣的數據庫中。

好比,有個廣告流量詳情表,每當用戶點擊廣告或者廣告展現出來的時候,爲了保證不丟失,這些數據都是實時插入到這個表裏的。

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

當有人點擊廣告了,他的點擊記錄會被傳到個人應用層,而後我會在應用層根據廣告 ID 作哈希,再根據哈希結果的不一樣,分別存到不一樣的數據庫中去。

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

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

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

 

5、

接下來,須要着手解決性能問題了。2PC 方案……算了……它瘋狂的一致性性格會要了個人狗命的。

那麼極端點,咱們不搞事務可不能夠呢?

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

當時,從業務上,要求廣告的訪問數據都要保證及時入庫不能丟,由於丟了就可能形成計費的損失,而這些損失全是錢。因此,每當用戶點擊廣告或者廣告展現出來的時候,爲了保證不丟失,這些數據都是實時入庫的。

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

若是徹底不考慮事務,則拆分庫後,操做可能會是這個樣子。

這三個操做可能會並行發往不一樣的數據庫執行。因爲三個操做之間沒有事務的約束,因此,一個操做出問題了,另外的操做並不會受到影響。

而這卻也引起了另一個問題,數據狀態不一致。

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

而這種不一致的狀態因爲已經被持久化到了數據庫中,就會致使這種不一致的狀態永久存在了數據庫中。這業務能接受嗎?但凡是有點職業精神的程序員能接受嗎?

要有折中的辦法!!!

 

6、

如今再回過頭來看看 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理論的基本可用、軟狀態和最終一致性了。

爲了寫這篇,又熬了好幾個夜,若是以爲寫的不賴,願意讓更多人看到,期待你的點贊和轉發。

(完)

相關文章
相關標籤/搜索