億級流量系統架構之如何支撐百億級數據的存儲與計算【石杉的架構筆記】

歡迎關注微信公衆號:石杉的架構筆記(id:shishan100)面試

個人新課**《C2C 電商系統微服務架構120天實戰訓練營》在公衆號儒猿技術窩**上線了,感興趣的同窗,能夠點擊下方連接瞭解詳情:算法

《C2C 電商系統微服務架構120天實戰訓練營》數據庫

**「**本文聊一下筆者幾年前所帶的團隊負責的多個項目中的其中一個,用這個項目來聊聊一個億級流量系統架構演進的過程。性能優化

1、背景引入

首先簡單介紹一下項目背景,公司對合做商家提供一個付費級產品,這個商業產品背後涉及到數百人的研發團隊協做開發,包括各類業務系統來提供不少強大的業務功能,同時在整個平臺中包含了一個相當重要的核心數據產品,這個數據產品的定位是全方位支持用戶的業務經營和快速決策。服務器

這篇文章就聊聊這個數據產品背後對應的一套大型商家數據平臺,看看這個平臺在分佈式、高併發、高可用、高性能、海量數據等技術挑戰下的架構演進歷程。微信

由於整套系統規模過於龐大,涉及研發人員不少,持續時間很長,文章難以表述出其中各類詳細的技術細節以及方案,所以本文主要從總體架構演進的角度來闡述。markdown

至於選擇這個商家數據平臺項目來聊架構演進過程,是由於這個平臺基本跟業務耦合度較低,不像咱們負責過的C端類的電商平臺以及其餘業務類平臺有那麼重的業務在裏面,文章能夠專一闡述技術架構的演進,不須要牽扯太多的業務細節。架構

此外,這個平臺項目在筆者帶的團隊負責過的衆多項目中,相對算比較簡單的,可是先後又涉及到各類架構的演進過程,所以很適合經過文字的形式來展示出來。併發

2、商家數據平臺的業務流程

下面幾點,是這個數據產品最核心的業務流程:app

  • 天天從用戶使用的大量業務系統中實時的採集過來各類業務數據
  • 接着存儲在本身的數據中內心
  • 而後實時的運算大量的幾百行~上千行的SQL來生成各類數據報表
  • 最後就能夠提供這些數據報表給用戶來分析。

基本上用戶在業務系統使用過程當中,只要數據一有變更,立馬就反饋到各類數據報表中,用戶立馬就能夠看到數據報表中的各類變化,進而快速的指導本身的決策和管理。

整個過程,你們看看下面的圖就明白了。

3、從0到1的過程當中上線的最low版本

看着上面那張圖好像很是的簡單,是否是?

彷佛數據平臺只要想個辦法把業務系統的數據採集過來,接着放在MySQL的各類表裏,直接咔嚓一下運行100多個幾百行的大SQL,而後SQL運行結果再寫到另一些MySQL的表裏做爲報表數據,接着用戶直接點擊報表頁面查詢MySQL裏的報表數據,就能夠了!

其實任何一個系統從0到1的過程,都是比較low的,剛開始爲了快速開發出來這個數據平臺,還真的就是用了這種架構來開發,你們看下面的圖。

其實在剛開始業務量很小,請求量很小,數據量很小的時候,上面那種架構也沒啥問題,還挺簡單的。

直接基於本身研發的數據庫binlog採集中間件(這個是另一套複雜系統了,不在本文討論的範圍裏,之後有機會能夠聊聊),感知各個業務系統的數據庫中的數據變動,毫秒級同步到數據平臺本身的MySQL庫裏;

接着數據平臺裏作一些定時調度任務,每隔幾秒鐘就運行上百個複雜大SQL,計算各類報表的數據並將結果存儲到MySQL庫中;

最後用戶只要對報表刷新一下,立馬就能夠從MySQL庫裏查到最新的報表數據。

基本上在無任何技術挑戰的前提下,這套簡易架構運行的會很順暢,效果很好。然而,事情每每不是咱們想的那麼簡單的,由於你們都知道國內那些互聯網巨頭公司最大的優點和資源之一,就是有豐富以及海量的C端用戶以及B端的合做商家。

論C端用戶,任何一個互聯網巨頭推出一個新的C端產品,極可能迅速就是上億用戶量;

論B端商家,任何一個互聯網巨頭若是打B端市場,憑藉巨大的影響力以及合做資源,極可能迅速就能夠聚攏數十萬,乃至上百萬的付費B端用戶。

所以不幸的是,接下來的一兩年內,這套系統將要面臨業務的高速增加帶來的巨大技術挑戰和壓力。

4、海量數據存儲和計算的技術挑戰

其實跟不少大型系統遇到的第一個技術挑戰同樣,這套系統遇到的第一個大問題,就是海量數據的存儲。

你一個系統剛開始上線也許就幾十個商家用,接着隨着大家產品的銷售持續大力推廣,可能幾個月內就會聚攏起來十萬級別的用戶。

這些用戶天天都會大量的使用你提供的產品,進而天天都會產生大量的數據,你們能夠想象一下,在數十萬規模的商家用戶使用場景下,天天你新增的數據量大概會是幾千萬條數據,**記住,這但是天天新增的數據!**這將會給上面你看到的那個很low的架構帶來巨大的壓力。

若是你在負責上面那套系統,結果慢慢的發現,天天都要涌入MySQL幾千萬條數據,這種現象是使人感到崩潰的,由於你的MySQL中的單表數據量會迅速膨脹,很快就會達到單表幾億條數據,甚至是數十億條數據,而後你對那些怪獸同樣的大表運行幾百行乃至上千行的SQL?其中包含了N層嵌套查詢以及N個各類多表鏈接?

我跟你打賭,若是你願意試一下,你會發現你的數據平臺系統直接卡死,由於一個大SQL可能都要幾個小時才能跑完。而後MySQL的cpu負載壓力直接100%,弄很差就把MySQL數據庫服務器給搞宕機了。

因此這就是第一個技術挑戰,數據量愈來愈大,SQL跑的愈來愈慢,MySQL服務器壓力愈來愈大。

咱們當時而言,已經看到了業務的快速增加,所以絕對要先業務一步來重構系統架構,不能讓上述狀況發生,第一次架構重構,勢在必行!

5、離線計算與實時計算的拆分

其實在幾年前咱們作這個項目的時候,大數據技術已經在國內開始運用的不錯了,並且尤爲在一些大型互聯網公司內,咱們基本上都運用大數據技術支撐過不少生產環境的項目了,在大數據這塊技術的經驗積累,也是足夠的。

針對這個數據產品的需求,咱們徹底能夠作到,將昨天以及昨天之前的數據都放在大數據存儲中,進行離線存儲和離線計算,而後只有今天的數據是實時的採集的。

所以在這種技術挑戰下,第一次架構重構的核心要義,就是將離線計算與實時計算進行拆分。

你們看上面那張圖,新的架構之下,分爲了離線與實時兩條計算鏈路。

**一條是離線計算鏈路:**天天凌晨,咱們將業務系統MySQL庫中的昨天之前的數據,做爲離線數據導入Hadoop HDFS中進行離線存儲,而後凌晨就基於Hive / Spark對離線存儲中的數據進行離線計算。

若是有同窗不清楚大數據的知識,能夠參加我以前寫的一篇文章:兄弟,用大白話告訴你小白都能看懂的Hadoop架構原理。Hadoop與Spark做爲世界上最優秀、運用最普遍的大數據技術,自然適合PB級海量數據的分佈式存儲和分佈式計算。

在離線計算鏈路全面採用大數據相關技術來支撐事後,完美解決了海量數據的存儲,哪怕你一天進來上億條數據都沒事,分佈式存儲能夠隨時擴容,同時基於分佈式計算技術自然適合海量數據的離線計算。

即便是天天凌晨耗費幾個小時將昨天之前的數據完成計算,這個也沒事,由於凌晨通常是沒人看這個數據的,因此主要在人家早上8點上班之前,完成數據計算就能夠了。

**另一條是實時計算鏈路:**天天零點事後,當天最新的數據變動,所有仍是走以前的老路子,秒級同步業務庫的數據到數據平臺存儲中,接着就是數據平臺系統定時運行大量的SQL進行計算。同時在天天零點的時候,還會從數據平臺的存儲中清理掉昨天的數據,僅僅保留當天一天的數據而已。

實時計算鏈路最大的改變,就是僅僅在數據平臺的本地存儲中保留當天一天的數據而已,這樣就大幅度下降了要放在MySQL中的數據量了。

舉個例子:好比一天就幾千萬條數據放在MySQL裏,那麼單表數據量被維持在了千萬的級別上,此時若是對SQL對應索引以及優化到極致以後,勉強仍是能夠在幾十秒內完成全部報表的計算。

6、持續增加的數據量和計算壓力

可是若是僅僅只是作到上面的架構,仍是隻能暫時性的緩解系統架構的壓力,由於業務還在加速狂飆,繼續增加。

你總是指望單日的數據量在千萬級別,怎麼可能?業務是不會給你這個機會的。很快就能夠預見到單日數據量將會達到幾億,甚至十億的級別。

若是一旦單日數據量達到了數十億的級別,單表數據量上億,你再怎麼優化SQL性能,有沒法保證100多個幾百行的複雜SQL能夠快速的運行完畢了。

到時候又會回到最初的問題,SQL計算過慢會致使數據平臺核心系統卡死,甚至給MySQL服務器過大壓力,CPU 100%負載後宕機。

並且此外還有另一個問題,那就是單個MySQL數據庫服務器的存儲容量是有限的,若是一旦單日數據量達到甚至超過了單臺MySQL數據庫服務器的存儲極限,那麼此時也會致使單臺MySQL數據庫沒法容納全部的數據了,這也是一個很大的問題!

第二次架構重構,勢在必行!

7、大數據領域的實時計算技術的缺陷

在幾年前作這個項目的背景下,當時可供選擇的大數據領域的實時計算技術,主要仍是Storm,算是比較成熟的一個技術,另外就是Spark生態裏的Spark Streaming。當時可沒有什麼如今較火的Flink、Druid等技術。

在仔細調研了一番事後發現,根本沒有任何一個大數據領域的實時計算技術能夠支撐這個需求。

由於Storm是不支持SQL的,並且即便勉強你讓他支持了,他的SQL支持也會很弱,徹底不可能運行幾百行甚至上千行的複雜SQL在這種流式計算引擎上的執行。

Spark Streaming也是同理,當時功能仍是比較弱小的,雖然能夠支持簡單SQL的執行,可是徹底沒法支持這種複雜SQL的精準運算。

所以很不幸的是,在當時的技術背景下,遇到的這個實時數據運算的痛點,沒有任何開源的技術是能夠解決的。必須得本身根據業務的具體場景,來從0開始定製開發本身的一套數據平臺系統架構。

8、分庫分表解決數據擴容問題

首先咱們要先解決第一個痛點,就是一旦單臺數據庫服務器沒法存儲下當日的數據,該怎麼辦?

第一個首選的方案固然就是分庫分表了。咱們須要將一個庫拆分爲多庫,不用的庫放在不一樣的數據庫服務器上,同時每一個庫裏放多張表。

採用這套分庫分表架構以後,能夠作到每一個數據庫服務器放一部分的數據,並且隨着數據量日益增加,能夠不斷地增長更多的數據庫服務器來容納更多的數據,作到按需擴容。

同時,每一個庫裏單表分爲多表,這樣能夠保證單表數據量不會太大,控制單表的數據量在幾百萬的量級,基本上性能優化到極致的SQL語句跑起來效率仍是不錯的,秒級出結果是能夠作到的。

一樣,給你們來一張圖,你們直觀的感覺一下:

9、讀寫分離下降數據庫服務器的負載

此時分庫分表以後,又面臨着另一個問題,就是如今若是對每一個數據庫服務器又是寫入又是讀取的話,會致使數據庫服務器的CPU負載和IO負載很是的高!

爲何這麼說呢?由於在此時寫數據庫的每秒併發已經達到幾千了,同時還頻繁的運行那種超大SQL來查詢數據,數據庫服務器的CPU運算會極其的繁忙。

所以咱們將MySQL作了讀寫分離的部署,每一個主數據庫服務器都掛了多個從數據庫服務器,寫只能寫入主庫,查能夠從從庫來查。

你們一塊兒來看看下面這張圖:

10、自研的滑動窗口動態計算引擎

可是光是作到這一點仍是不夠的,由於其實在生產環境發現,哪怕單表數據量限制在了幾百萬的級別,你運行幾百個幾百行復雜SQL,也要幾十秒甚至幾分鐘的時間,這個時效性對付費級的產品已經有點沒法接受,產品提出的極致性能要求是,秒級!

所以對上述系統架構,咱們再次作了架構的優化,在數據平臺中嵌入了本身純自研的滑動窗口計算引擎,核心思想以下:

  1. 在數據庫binlog採集中間件採集的過程當中,要將數據的變動切割爲一個一個的滑動時間窗口,每一個滑動時間窗口爲幾秒鐘,對每一個窗口內的數據打上那個窗口的標籤
  2. 同時須要維護一份滑動時間窗口的索引數據,包括每一個分片的數據在哪一個窗口裏,每一個窗口的數據的一些具體的索引信息和狀態
  3. 接着數據平臺中的核心計算引擎,再也不是每隔幾十秒就運行大量SQL對當天全部的數據所有計算一遍了,而是對一個接一個的滑動時間窗口,根據窗口標籤提取出那個窗口內的數據進行計算,計算的僅僅是最近一個滑動時間窗口內的數據
  4. 接着對這個滑動時間窗口內的數據,可能最多就千條左右吧,運行全部的複雜SQL計算出這個滑動時間窗口內的報表數據,而後將這個窗口數據計算出的結果,與以前計算出來的其餘窗口內的計算結果進行合併,最後放入MySQL中的報表內
  5. 此外,這裏須要考慮到一系列的生產級機制,包括滑動時間窗口若是計算失敗怎麼辦?若是一個滑動時間窗口計算過慢怎麼辦?滑動窗口計算過程當中系統宕機瞭如何在重啓以後自動恢復計算?等等

經過這套滑動窗口的計算引擎,咱們直接將系統計算性能提高了幾十倍,基本上每一個滑動窗口的數據只要幾秒鐘就能夠完成所有報表的計算,至關於一會兒把最終呈現給用戶的實時數據的時效性提高到了幾秒鐘,而不是幾十秒。

一樣,你們看看下面的圖。

11、離線計算鏈路的性能優化

實時計算鏈路的性能問題經過自研滑動窗口計算引擎來解決了,可是離線計算鏈路此時又出現了性能問題。。。

由於天天凌晨從業務庫中離線導入的是歷史全量數據,接着須要在凌晨針對百億量級的全量數據,運行不少複雜的上千行復雜SQL來進行運算,當數據量達到百億以後,這個過程耗時很長,有時候要從凌晨一直計算到上午。

關鍵問題就在於,離線計算鏈路,天天都是導入全量數據來進行計算,這就很坑了。

之因此這麼作,是由於從業務庫同步數據時,天天都涉及到數據的更新操做,而hadoop裏的數據是無法跟業務庫那樣來進行更新的,所以最開始都是天天導入全量歷史數據,做爲一個最新快照來進行全量計算。

在這裏,咱們對離線計算鏈路進行了優化,**主要就是全量計算轉增量計算:**天天數據在導入hadoop以後,都會針對數據的業務時間戳來分析和提取出來天天變動過的增量數據,將這些增量數據放入獨立的增量數據表中。

同時須要根據具體的業務需求,自動分析數據計算的基礎血緣關係,有可能增量數據須要與部分全量數據混合才能完成計算,此時可能會提取部分全量歷史數據,合併完成計算。計算完成以後,將計算結果與歷史計算結果進行合併。

在完成這個全量計算轉增量計算的過程以後,離線計算鏈路在凌晨基本上百億級別的數據量,只要對昨天的增量數據花費一兩個小時完成計算以後,就能夠完成離線計算的所有任務,性能相較於全量計算提高至少十倍以上。

12、階段性總結

到此爲止,就是這套系統在最初一段時間作出來的一套架構,不算太複雜,還有不少缺陷,不完美,可是在當時的業務背景下效果至關的不錯。

在這套架構對應的早期業務背景下,天天新增數據大概是億級左右,可是分庫分表以後,單表數據量在百萬級別,單臺數據庫服務器的高峯期寫入壓力在2000/s,查詢壓力在100/s,數據庫集羣承載的總高峯寫入壓力在1萬/s,查詢壓力在500/s,有須要還能夠隨時擴容更多的數據庫服務器,承載更多的數據量,更高的寫入併發與查詢併發。

並且,由於作了讀寫分離,所以每一個數據庫服務器的CPU負載和IO負載都不會在高峯期打滿,避免數據庫服務器的負載太高。

而基於滑動時間窗口的自研計算引擎,能夠保證當天更新的實時數據主要幾秒鐘就能夠完成一個微批次的計算,反饋到用戶看到的數據報表中。

同時這套引擎自行管理着計算的狀態與日誌,若是出現某個窗口的計算失敗、系統宕機、計算超時,等各類異常的狀況,這個套引擎能夠自動重試與恢復。

此外,昨天之前的海量數據都是走Hadoop與Spark生態的離線存儲與計算。通過性能優化以後,天天凌晨花費一兩個小時,算好昨天之前全部的數據便可。

最後實時與離線的計算結果在同一個MySQL數據庫中融合,此時用戶若是對業務系統作出操做,實時數據報表在幾秒後就會刷新,若是要看昨天之前的數據能夠隨時選擇時間範圍查看便可,暫時性是知足了業務的需求。

早期的幾個月裏,日增上億數據,離線與實時兩條鏈路中的總體數據量級達到了百億級別,不管是存儲擴容,仍是高效計算,這套架構基本是撐住了。

十3、下一階段的展望

這個大型系統架構演進實踐是一個系列的文章,將會包含不少篇文章,由於一個大型的系統架構演進的過程,會持續很長時間,作出不少次的架構升級與重構,不斷的解決日益增加的技術挑戰,最終完美的抗住海量數據、高併發、高性能、高可用等場景。

下一篇文章會說說下一步是如何將數據平臺系統重構爲一套高可用高容錯的分佈式系統架構的,來解決單點故障、單系統CPU負載太高、自動故障轉移、自動數據容錯等相關的問題。包括以後還會有多篇文章涉及到咱們自研的更加複雜的支撐高併發、高可用、高性能、海量數據的平臺架構。

十4、上篇文章的答疑

上一篇文章寫了一個分佈式鎖的高併發優化的文章,具體參見:每秒上千訂單場景下的分佈式鎖高併發優化實踐!。收到了你們不少的提問,其實最終都是一個問題:

針對那篇文章裏的用分佈式鎖的分段加鎖的方式,解決庫存超賣問題,那若是一個分段的庫存不知足要購買的數量,怎麼辦?

第一,我當時文章裏提了一句,可能沒寫太詳細,若是一個分段庫存不足,要鎖其餘的分段,進行合併扣減,若是你作分段加鎖,那就是這樣的,很麻煩。

若是你們去看看Java 8裏的LongAdder的源碼,他的分段加鎖的優化,也是如此的麻煩,要作段遷移。

第二,我在那篇文章裏反覆強調了一下,不要對號入座,由於實際的電商庫存超賣問題,有不少其餘的技術手段,咱們就用的是其餘的方案,不是這個方案,之後有機會給你們專門講如何解決電商庫存超賣問題。

那篇文章僅僅是用那個例做爲一個業務案例而已,闡述一下分佈式鎖的併發問題,以及高併發的優化手段,方便你們來理解那個意思,僅此而已。

第三,最後再強調一下,你們關注分段加鎖的思想就好,切記不要對號入座,不要關注過多在庫存超賣業務上了。

END

敬請期待:

《億級流量系統架構之如何設計高容錯分佈式計算系統》

《億級流量系統架構之如何設計承載百億流量的高性能架構》

《億級流量系統架構之如何設計每秒數十萬查詢的高併發架構》

《億級流量系統架構之如何設計全鏈路99.99%高可用架構》

若有收穫,請幫忙轉發,您的鼓勵是做者最大的動力,謝謝!

一大波微服務、分佈式、高併發、高可用的****原創系列

文章正在路上,歡迎掃描下方二維碼,持續關注:

石杉的架構筆記(id:shishan100)

十餘年BAT架構經驗傾囊相授

**推薦閱讀:

一、拜託!面試請不要再問我Spring Cloud底層原理

二、【雙11狂歡的背後】微服務註冊中心如何承載大型系統的千萬級訪問?

三、【性能優化之道】每秒上萬併發下的Spring Cloud參數優化實戰

四、微服務架構如何保障雙11狂歡下的99.99%高可用

五、兄弟,用大白話告訴你小白都能聽懂的Hadoop架構原理

六、大規模集羣下Hadoop NameNode如何承載每秒上千次的高併發訪問

七、【性能優化的祕密】Hadoop如何將TB級大文件的上傳性能優化上百倍

八、拜託,面試請不要再問我TCC分佈式事務的實現原理坑爹呀!

九、【坑爹呀!】最終一致性分佈式事務如何保障實際生產中99.99%高可用?

十、拜託,面試請不要再問我Redis分佈式鎖的實現原理!**

十一、****【眼前一亮!】看Hadoop底層算法如何優雅的將大規模集羣性能提高10倍以上?

十二、每秒上千訂單場景下的分佈式鎖高併發優化實踐!

相關文章
相關標籤/搜索