01 | 高併發系統:它的通用設計方法是什麼?

我們知道,高併發代表着大流量,高併發系統設計的魅力就在於我們能夠憑藉自己的聰明才智設計巧妙的方案,從而抵抗巨大流量的衝擊,帶給用戶更好的使用體驗。這些方案好似能操縱流量,讓流量更加平穩地被系統中的服務和組件處理。
來做個簡單的比喻吧。
從古至今,長江和黃河流域水患不斷,遠古時期大禹曾拓寬河道,清除淤沙讓流水更加順暢;都江堰作爲史上最成功的治水案例之一,用引流將岷江之水分流到多個支流中,以分擔水流壓力;三門峽和葛洲壩通過建造水庫將水引入水庫先存儲起來,然後再想辦法把水庫中的水緩緩地排出去,以此提高下游的抗洪能力。
而我們在應對高併發大流量時也會採用類似“抵禦洪水”的方案,歸納起來共有三種方法。

Scale-out(橫向擴展):分而治之是一種常見的高併發系統設計方法,採用分佈式部署的方式把流量分流開,讓每個服務器都承擔一部分併發和流量。

緩存:使用緩存來提高系統的性能,就好比用“拓寬河道”的方式抵抗高併發大流量的衝擊。

異步:在某些場景下,未處理完成之前我們可以讓請求先返回,在數據準備好之後再通知請求方,這樣可以在單位時間內處理更多的請求。

簡單介紹了這三種方法之後,我再詳細地帶你瞭解一下,這樣當你在設計高併發系統時就可以有考慮的方向了。當然了,這三種方法會細化出更多的內容,我會在後面的課程中深入講解。

首先,我們先來了解第一種方法:Scale-out。

Scale-up vs Scale-out
著名的“摩爾定律”是由 Intel 的創始人之一戈登·摩爾於 1965 年提出的。這個定律提到集成電路上可容納的晶體管的數量約每隔兩年會增加一倍。
後來,Intel 首席執行官大衛·豪斯提出“18 個月”的說法,即預計 18 個月會將芯片的性能提升一倍,這個說法廣爲流傳。
摩爾定律雖然描述的是芯片的發展速度,但我們可以延伸爲整體的硬件性能,從 20 世紀後半葉開始,計算機硬件的性能是指數級演進的。
直到現在,摩爾定律依然生效,在半個世紀以來的 CPU 發展過程中,芯片廠商靠着在有限面積上做更小的晶體管的黑科技,大幅度地提升着芯片的性能。從第一代集成電路上只有十幾個晶體管,到現在一個芯片上動輒幾十億晶體管的數量,摩爾定律指引着芯片廠商完成了技術上的飛躍。

但是有專家預測,摩爾定律可能在未來幾年之內不再生效,原因是目前的芯片技術已經做到了 5nm 級別,在工藝上可以突破的空間不大,可能達不到摩爾定律提到的每 18 個月翻一番的速度了。此時,雙核和多核技術的產生拯救了摩爾定律,這些技術的思路是將多個 CPU 核心壓在一個芯片上,從而大大提升 CPU 的並行處理能力。

我們在高併發系統設計上也沿用了同樣的思路,將類似追逐摩爾定律不斷提升 CPU 性能的方案叫做 Scale-up(縱向擴展),把類似 CPU 多核心的方案叫做 Scale-out,這兩種思路在實現方式上是完全不同的。

Scale-up 通過購買性能更好的硬件來提升系統的併發處理能力,比方說目前系統 4 核 4G 每秒可以處理 200 次請求,那麼如果要處理 400 次請求呢?很簡單,我們把機器的硬件提升到 8 核 8G(硬件資源的提升可能不是線性的,這裏僅爲參考)。

Scale-out 則是另外一個思路,它通過將多個低性能的機器組成一個分佈式集羣來共同抵禦高併發流量的衝擊。沿用剛纔的例子,我們可以使用兩臺 4 核 4G 的機器來處理那 400 次請求。

那麼什麼時候選擇 Scale-up,什麼時候選擇 Scale-out 呢?一般來講,在我們系統設計初期會考慮使用 Scale-up 的方式,因爲這種方案足夠簡單,所謂能用堆砌硬件解決的問題就用硬件來解決,但是當系統併發超過了單機的極限時,我們就要使用 Scale-out 的方式。

Scale-out 雖然能夠突破單機的限制,但也會引入一些複雜問題。比如,如果某個節點出現故障如何保證整體可用性?當多個節點有狀態需要同步時如何保證狀態信息在不同節點的一致性?如何做到使用方無感知的增加和刪除節點?其中每一個問題都涉及很多的知識點,我會在後面的課程中深入地講解,這裏暫時不展開了。'

說完了 Scale-out,我們再來看看高併發系統設計的另一種方法:緩存。

使用緩存提升性能

Web 2.0 是緩存的時代,這一點毋庸置疑。緩存遍佈在系統設計的每個角落,從操作系統到瀏覽器,從數據庫到消息隊列,任何略微複雜的服務和組件中你都可以看到緩存的影子。我們使用緩存的主要作用是提升系統的訪問性能,在高併發的場景下就可以支撐更多用戶的同時訪問。

那麼爲什麼緩存可以大幅度提升系統的性能呢?我們知道數據是放在持久化存儲中的,一般的持久化存儲都是使用磁盤作爲存儲介質的,而普通磁盤數據由機械手臂、磁頭、轉軸、盤片組成,盤片又分爲磁道、柱面和扇區,盤片構造圖我放在下面了。

盤片是存儲介質,每個盤片被劃分爲多個同心圓,信息都被存儲在同心圓之中,這些同心圓就是磁道。在磁盤工作時盤片是在高速旋轉的,機械手臂驅動磁頭沿着徑向移動,在磁道上讀取所需要的數據。我們把磁頭尋找信息花費的時間叫做尋道時間。

普通磁盤的尋道時間是 10ms 左右,而相比於磁盤尋道花費的時間,CPU 執行指令和內存尋址的時間都是在 ns(納秒)級別,從千兆網卡上讀取數據的時間是在μs(微秒)級別。所以在整個計算機體系中磁盤是最慢的一環,甚至比其它的組件要慢幾個數量級。因此我們通常使用以內存作爲存儲介質的緩存,以此提升性能。

當然,緩存的語義已經豐富了很多,我們可以將任何降低響應時間的中間存儲都稱爲緩存。緩存的思想遍佈很多設計領域,比如在操作系統中 CPU 有多級緩存,文件有 Page Cache 緩存,你應該有所瞭解。

異步處理

異步也是一種常見的高併發設計方法,我們在很多文章和演講中都能聽到這個名詞,與之共同出現的還有它的反義詞:同步。比如分佈式服務框架 Dubbo 中有同步方法調用和異步方法調用,IO 模型中有同步 IO 和異步 IO。

那麼什麼是同步,什麼是異步呢?以方法調用爲例,同步調用代表調用方要阻塞等待被調用方法中的邏輯執行完成。這種方式下,當被調用方法響應時間較長時,會造成調用方長久的阻塞,在高併發下會造成整體系統性能下降甚至發生雪崩。

異步調用恰恰相反,調用方不需要等待方法邏輯執行完成就可以返回執行其他的邏輯,在被調用方法執行完畢後再通過回調、事件通知等方式將結果反饋給調用方。

異步調用在大規模高併發系統中被大量使用,比如我們熟知的 12306 網站。當我們訂票時,頁面會顯示系統正在排隊,這個提示就代表着系統在異步處理我們的訂票請求。在 12306 系統中查詢餘票、下單和更改餘票狀態都是比較耗時的操作,可能涉及多個內部系統的互相調用,如果是同步調用就會像 12306 剛剛上線時那樣,高峯期永遠不可能下單成功。

而採用異步的方式,後端處理時會把請求丟到消息隊列中,同時快速響應用戶,告訴用戶我們正在排隊處理,然後釋放出資源來處理更多的請求。訂票請求處理完之後,再通知用戶訂票成功或者失敗。

[editor,running]

處理邏輯後移到異步處理程序中,Web 服務的壓力小了,資源佔用的少了,自然就能接收更多的用戶訂票請求,系統承受高併發的能力也就提升了。

既然我們瞭解了這三種方法,那麼是不是意味着在高併發系統設計中,開發一個系統時要把這些方法都用上呢?當然不是,系統的設計是不斷演進的。''

羅馬不是一天建成的,系統的設計也是如此。不同量級的系統有不同的痛點,也就有不同的架構設計的側重點。如果都按照百萬、千萬併發來設計系統,電商一律向淘寶看齊,IM 全都學習微信和 QQ,那麼這些系統的命運一定是滅亡。

因爲淘寶、微信的系統雖然能夠解決同時百萬、千萬人同時在線的需求,但其內部的複雜程度也遠非我們能夠想象的。盲目地追從只能讓我們的架構複雜不堪,最終難以維護。就拿從單體架構往服務化演進來說,淘寶也是在經歷了多年的發展後,發現系統整體的擴展能力出現問題時,開始啓動服務化改造項目的。

我之前也踩過一些坑,參與的一個創業項目在初始階段就採用了服務化的架構,但由於當時人力有限,團隊技術積累不足,因此在實際項目開發過程中,發現無法駕馭如此複雜的架構,也出現了問題難以定位、系統整體性能下降等多方面的問題,甚至連繫統宕機了都很難追查到根本原因,最後不得不把服務做整合,迴歸到簡單的單體架構中。

所以我建議一般系統的演進過程應該遵循下面的思路:

最簡單的系統設計滿足業務需求和流量現狀,選擇最熟悉的技術體系。

隨着流量的增加和業務的變化修正架構中存在問題的點,如單點問題、橫向擴展問題、性能無法滿足需求的組件。在這個過程中,選擇社區成熟的、團隊熟悉的組件幫助我們解決問題,在社區沒有合適解決方案的前提下才會自己造輪子。

當對架構的小修小補無法滿足需求時,考慮重構、重寫等大的調整方式以解決現有的問題。

以淘寶爲例,當時在業務從 0 到 1 的階段是通過購買的方式快速搭建了系統。而後,隨着流量的增長,淘寶做了一系列的技術改造來提升高併發處理能力,比如數據庫存儲引擎從 MyISAM 遷移到 InnoDB,數據庫做分庫分表,增加緩存,啓動中間件研發等。

當這些都無法滿足時就考慮對整體架構做大規模重構,比如說著名的“五彩石”項目讓淘寶的架構從單體演進爲服務化架構。正是通過逐步的技術演進,淘寶才進化出如今承擔過億 QPS 的技術架構。

歸根結底一句話:高併發系統的演進應該是循序漸進,以解決系統中存在的問題爲目的和驅動力的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章