億流量大考:日增上億數(shù)據(jù),把MySQL直接搞宕機(jī)了
一、背景引入
首先簡(jiǎn)單介紹一下項(xiàng)目背景,公司對(duì)合作商家提供一個(gè)付費(fèi)級(jí)產(chǎn)品,這個(gè)商業(yè)產(chǎn)品背后涉及到數(shù)百人的研發(fā)團(tuán)隊(duì)協(xié)作開(kāi)發(fā),包括各種業(yè)務(wù)系統(tǒng)來(lái)提供很多強(qiáng)大的業(yè)務(wù)功能,同時(shí)在整個(gè)平臺(tái)中包含了一個(gè)至關(guān)重要的核心數(shù)據(jù)產(chǎn)品,這個(gè)數(shù)據(jù)產(chǎn)品的定位是全方位支持用戶(hù)的業(yè)務(wù)經(jīng)營(yíng)和快速?zèng)Q策。
這篇文章就聊聊這個(gè)數(shù)據(jù)產(chǎn)品背后對(duì)應(yīng)的一套大型商家數(shù)據(jù)平臺(tái),看看這個(gè)平臺(tái)在分布式、高并發(fā)、高可用、高性能、海量數(shù)據(jù)等技術(shù)挑戰(zhàn)下的架構(gòu)演進(jìn)歷程。
因?yàn)檎紫到y(tǒng)規(guī)模過(guò)于龐大,涉及研發(fā)人員很多,持續(xù)時(shí)間很長(zhǎng),文章難以表述出其中各種詳細(xì)的技術(shù)細(xì)節(jié)以及方案,因此本文主要從整體架構(gòu)演進(jìn)的角度來(lái)闡述。
至于選擇這個(gè)商家數(shù)據(jù)平臺(tái)項(xiàng)目來(lái)聊架構(gòu)演進(jìn)過(guò)程,是因?yàn)檫@個(gè)平臺(tái)基本跟業(yè)務(wù)耦合度較低,不像我們負(fù)責(zé)過(guò)的C端類(lèi)的電商平臺(tái)以及其他業(yè)務(wù)類(lèi)平臺(tái)有那么重的業(yè)務(wù)在里面,文章可以專(zhuān)注闡述技術(shù)架構(gòu)的演進(jìn),不需要牽扯太多的業(yè)務(wù)細(xì)節(jié)。
此外,這個(gè)平臺(tái)項(xiàng)目在筆者帶的團(tuán)隊(duì)負(fù)責(zé)過(guò)的眾多項(xiàng)目中,相對(duì)算比較簡(jiǎn)單的,但是前后又涉及到各種架構(gòu)的演進(jìn)過(guò)程,因此很適合通過(guò)文字的形式來(lái)展現(xiàn)出來(lái)。
二、商家數(shù)據(jù)平臺(tái)的業(yè)務(wù)流程
下面幾點(diǎn),是這個(gè)數(shù)據(jù)產(chǎn)品最核心的業(yè)務(wù)流程:
- 每天從用戶(hù)使用的大量業(yè)務(wù)系統(tǒng)中實(shí)時(shí)的采集過(guò)來(lái)各種業(yè)務(wù)數(shù)據(jù)
- 接著存儲(chǔ)在自己的數(shù)據(jù)中心里
- 然后實(shí)時(shí)的運(yùn)算大量的幾百行~上千行的SQL來(lái)生成各種數(shù)據(jù)報(bào)表
- 最后就可以提供這些數(shù)據(jù)報(bào)表給用戶(hù)來(lái)分析。
基本上用戶(hù)在業(yè)務(wù)系統(tǒng)使用過(guò)程中,只要數(shù)據(jù)一有變動(dòng),立馬就反饋到各種數(shù)據(jù)報(bào)表中,用戶(hù)立馬就可以看到數(shù)據(jù)報(bào)表中的各種變化,進(jìn)而快速的指導(dǎo)自己的決策和管理。
整個(gè)過(guò)程,大家看看下面的圖就明白了。
三、從0到1的過(guò)程中上線的最low版本
看著上面那張圖好像非常的簡(jiǎn)單,是不是?
看整個(gè)過(guò)程,似乎數(shù)據(jù)平臺(tái)只要想個(gè)辦法把業(yè)務(wù)系統(tǒng)的數(shù)據(jù)采集過(guò)來(lái),接著放在MySQL的各種表里,直接咔嚓一下運(yùn)行100多個(gè)幾百行的大SQL,然后SQL運(yùn)行結(jié)果再寫(xiě)到另外一些MySQL的表里作為報(bào)表數(shù)據(jù),接著用戶(hù)直接點(diǎn)擊報(bào)表頁(yè)面查詢(xún)MySQL里的報(bào)表數(shù)據(jù),就可以了!
其實(shí)任何一個(gè)系統(tǒng)從0到1的過(guò)程,都是比較low的,剛開(kāi)始為了快速開(kāi)發(fā)出來(lái)這個(gè)數(shù)據(jù)平臺(tái),還真的就是用了這種架構(gòu)來(lái)開(kāi)發(fā),大家看下面的圖。
其實(shí)在剛開(kāi)始業(yè)務(wù)量很小,請(qǐng)求量很小,數(shù)據(jù)量很小的時(shí)候,上面那種架構(gòu)也沒(méi)啥問(wèn)題,還挺簡(jiǎn)單的。
我們直接基于自己研發(fā)的數(shù)據(jù)庫(kù)binlog采集中間件(這個(gè)是另外一套復(fù)雜系統(tǒng)了,不在本文討論的范圍里,以后有機(jī)會(huì)可以聊聊),感知各個(gè)業(yè)務(wù)系統(tǒng)的數(shù)據(jù)庫(kù)中的數(shù)據(jù)變更,毫秒級(jí)同步到數(shù)據(jù)平臺(tái)自己的MySQL庫(kù)里;
接著數(shù)據(jù)平臺(tái)里做一些定時(shí)調(diào)度任務(wù),每隔幾秒鐘就運(yùn)行上百個(gè)復(fù)雜大SQL,計(jì)算各種報(bào)表的數(shù)據(jù)并將結(jié)果存儲(chǔ)到MySQL庫(kù)中;
最后用戶(hù)只要對(duì)報(bào)表刷新一下,立馬就可以從MySQL庫(kù)里查到最新的報(bào)表數(shù)據(jù)。
基本上在無(wú)任何技術(shù)挑戰(zhàn)的前提下,這套簡(jiǎn)易架構(gòu)運(yùn)行的會(huì)很順暢,效果很好。然而,事情往往不是我們想的那么簡(jiǎn)單的,因?yàn)榇蠹叶贾绹?guó)內(nèi)那些互聯(lián)網(wǎng)巨頭公司最大的優(yōu)勢(shì)和資源之一,就是有豐富以及海量的C端用戶(hù)以及B端的合作商家。
對(duì)C端用戶(hù),任何一個(gè)互聯(lián)網(wǎng)巨頭推出一個(gè)新的C端產(chǎn)品,很可能迅速就是上億用戶(hù)量;
對(duì)B端商家,任何一個(gè)互聯(lián)網(wǎng)巨頭如果打B端市場(chǎng),憑借巨大的影響力以及合作資源,很可能迅速就可以聚攏數(shù)十萬(wàn),乃至上百萬(wàn)的付費(fèi)B端用戶(hù)。
因此,很不幸,接下來(lái)的一兩年內(nèi),這套系統(tǒng)將要面臨業(yè)務(wù)的高速增長(zhǎng)帶來(lái)的巨大技術(shù)挑戰(zhàn)和壓力。
四、海量數(shù)據(jù)存儲(chǔ)和計(jì)算的技術(shù)挑戰(zhàn)
其實(shí)跟很多大型系統(tǒng)遇到的第一個(gè)技術(shù)挑戰(zhàn)一樣,這套系統(tǒng)遇到的第一個(gè)大問(wèn)題,就是海量數(shù)據(jù)的存儲(chǔ)。
你一個(gè)系統(tǒng)剛開(kāi)始上線也許就幾十個(gè)商家用,接著隨著你們產(chǎn)品的銷(xiāo)售持續(xù)大力推廣,可能幾個(gè)月內(nèi)就會(huì)聚攏起來(lái)十萬(wàn)級(jí)別的用戶(hù)。
這些用戶(hù)每天都會(huì)大量的使用你提供的產(chǎn)品,進(jìn)而每天都會(huì)產(chǎn)生大量的數(shù)據(jù),大家可以想象一下,在數(shù)十萬(wàn)規(guī)模的商家用戶(hù)使用場(chǎng)景下,每天你新增的數(shù)據(jù)量大概會(huì)是幾千萬(wàn)條數(shù)據(jù),記住,這可是每天新增的數(shù)據(jù)!這將會(huì)給上面你看到的那個(gè)很low的架構(gòu)帶來(lái)巨大的壓力。
如果你在負(fù)責(zé)上面那套系統(tǒng),結(jié)果慢慢的發(fā)現(xiàn),每天都要涌入MySQL幾千萬(wàn)條數(shù)據(jù),這種現(xiàn)象是令人感到崩潰的,因?yàn)槟愕腗ySQL中的單表數(shù)據(jù)量會(huì)迅速膨脹,很快就會(huì)達(dá)到單表幾億條數(shù)據(jù),甚至是數(shù)十億條數(shù)據(jù),然后你對(duì)那些怪獸一樣的大表運(yùn)行幾百行乃至上千行的SQL?其中包含了N層嵌套查詢(xún)以及N個(gè)各種多表連接?
我跟你打賭,如果你愿意試一下,你會(huì)發(fā)現(xiàn)你的數(shù)據(jù)平臺(tái)系統(tǒng)直接卡死,因?yàn)橐粋€(gè)大SQL可能都要幾個(gè)小時(shí)才能跑完。然后MySQL的cpu負(fù)載壓力直接100%,弄不好就把MySQL數(shù)據(jù)庫(kù)服務(wù)器給搞宕機(jī)了。
所以這就是第一個(gè)技術(shù)挑戰(zhàn),數(shù)據(jù)量越來(lái)越大,SQL跑的越來(lái)越慢,MySQL服務(wù)器壓力越來(lái)越大。
我們當(dāng)時(shí)而言,已經(jīng)看到了業(yè)務(wù)的快速增長(zhǎng),因此絕對(duì)要先業(yè)務(wù)一步來(lái)重構(gòu)系統(tǒng)架構(gòu),不能讓上述情況發(fā)生,第一次架構(gòu)重構(gòu),勢(shì)在必行!
五、離線計(jì)算與實(shí)時(shí)計(jì)算的拆分
其實(shí)在幾年前我們做這個(gè)項(xiàng)目的時(shí)候,大數(shù)據(jù)技術(shù)已經(jīng)在國(guó)內(nèi)開(kāi)始運(yùn)用得不錯(cuò)了,而且尤其在一些大型互聯(lián)網(wǎng)公司內(nèi),我們基本上都運(yùn)用大數(shù)據(jù)技術(shù)支撐過(guò)很多生產(chǎn)環(huán)境的項(xiàng)目了,在大數(shù)據(jù)這塊技術(shù)的經(jīng)驗(yàn)積累,也是足夠的。
針對(duì)這個(gè)數(shù)據(jù)產(chǎn)品的需求,我們完全可以做到,將昨天以及昨天以前的數(shù)據(jù)都放在大數(shù)據(jù)存儲(chǔ)中,進(jìn)行離線存儲(chǔ)和離線計(jì)算,然后只有今天的數(shù)據(jù)是實(shí)時(shí)的采集的。
因此在這種技術(shù)挑戰(zhàn)下,第一次架構(gòu)重構(gòu)的核心要義,就是將離線計(jì)算與實(shí)時(shí)計(jì)算進(jìn)行拆分。
大家看上面那張圖,新的架構(gòu)之下,分為了離線與實(shí)時(shí)兩條計(jì)算鏈路。
一條是離線計(jì)算鏈路:每天凌晨,我們將業(yè)務(wù)系統(tǒng)MySQL庫(kù)中的昨天以前的數(shù)據(jù),作為離線數(shù)據(jù)導(dǎo)入Hadoop HDFS中進(jìn)行離線存儲(chǔ),然后凌晨就基于Hive / Spark對(duì)離線存儲(chǔ)中的數(shù)據(jù)進(jìn)行離線計(jì)算。
在離線計(jì)算鏈路全面采用大數(shù)據(jù)相關(guān)技術(shù)來(lái)支撐過(guò)后,完美解決了海量數(shù)據(jù)的存儲(chǔ),哪怕你一天進(jìn)來(lái)上億條數(shù)據(jù)都沒(méi)事,分布式存儲(chǔ)可以隨時(shí)擴(kuò)容,同時(shí)基于分布式計(jì)算技術(shù)天然適合海量數(shù)據(jù)的離線計(jì)算。
即使是每天凌晨耗費(fèi)幾個(gè)小時(shí)將昨天以前的數(shù)據(jù)完成計(jì)算,這個(gè)也沒(méi)事,因?yàn)榱璩恳话闶菦](méi)人看這個(gè)數(shù)據(jù)的,所以主要在人家早上8點(diǎn)上班以前,完成數(shù)據(jù)計(jì)算就可以了。
另外一條是實(shí)時(shí)計(jì)算鏈路:每天零點(diǎn)過(guò)后,當(dāng)天最新的數(shù)據(jù)變更,全部還是走之前的老路子,秒級(jí)同步業(yè)務(wù)庫(kù)的數(shù)據(jù)到數(shù)據(jù)平臺(tái)存儲(chǔ)中,接著就是數(shù)據(jù)平臺(tái)系統(tǒng)定時(shí)運(yùn)行大量的SQL進(jìn)行計(jì)算。同時(shí)在每天零點(diǎn)的時(shí)候,還會(huì)從數(shù)據(jù)平臺(tái)的存儲(chǔ)中清理掉昨天的數(shù)據(jù),僅僅保留當(dāng)天一天的數(shù)據(jù)而已。
實(shí)時(shí)計(jì)算鏈路最大的改變,就是僅僅在數(shù)據(jù)平臺(tái)的本地存儲(chǔ)中保留當(dāng)天一天的數(shù)據(jù)而已,這樣就大幅度降低了要放在MySQL中的數(shù)據(jù)量了。
舉個(gè)例子:比如一天就幾千萬(wàn)條數(shù)據(jù)放在MySQL里,那么單表數(shù)據(jù)量被維持在了千萬(wàn)的級(jí)別上,此時(shí)如果對(duì)SQL對(duì)應(yīng)索引以及優(yōu)化到極致之后,勉強(qiáng)還是可以在幾十秒內(nèi)完成所有報(bào)表的計(jì)算。
六、持續(xù)增長(zhǎng)的數(shù)據(jù)量和計(jì)算壓力
但是如果僅僅只是做到上面的架構(gòu),還是只能暫時(shí)性的緩解系統(tǒng)架構(gòu)的壓力,因?yàn)闃I(yè)務(wù)還在加速狂飆,繼續(xù)增長(zhǎng)。
你老是期望單日的數(shù)據(jù)量在千萬(wàn)級(jí)別,怎么可能?業(yè)務(wù)是不會(huì)給你這個(gè)機(jī)會(huì)的。很快就可以預(yù)見(jiàn)到單日數(shù)據(jù)量將會(huì)達(dá)到幾億,甚至十億的級(jí)別。
如果一旦單日數(shù)據(jù)量達(dá)到了數(shù)十億的級(jí)別,單表數(shù)據(jù)量上億,你再怎么優(yōu)化SQL性能,有無(wú)法保證100多個(gè)幾百行的復(fù)雜SQL可以快速的運(yùn)行完畢了。
到時(shí)候又會(huì)回到最初的問(wèn)題,SQL計(jì)算過(guò)慢會(huì)導(dǎo)致數(shù)據(jù)平臺(tái)核心系統(tǒng)卡死,甚至給MySQL服務(wù)器過(guò)大壓力,CPU 100%負(fù)載后宕機(jī)。
而且此外還有另外一個(gè)問(wèn)題,那就是單個(gè)MySQL數(shù)據(jù)庫(kù)服務(wù)器的存儲(chǔ)容量是有限的,如果一旦單日數(shù)據(jù)量達(dá)到甚至超過(guò)了單臺(tái)MySQL數(shù)據(jù)庫(kù)服務(wù)器的存儲(chǔ)極限,那么此時(shí)也會(huì)導(dǎo)致單臺(tái)MySQL數(shù)據(jù)庫(kù)無(wú)法容納所有的數(shù)據(jù)了,這也是一個(gè)很大的問(wèn)題!
第二次架構(gòu)重構(gòu),勢(shì)在必行!
七、大數(shù)據(jù)領(lǐng)域的實(shí)時(shí)計(jì)算技術(shù)的缺陷
在幾年前做這個(gè)項(xiàng)目的背景下,當(dāng)時(shí)可供選擇的大數(shù)據(jù)領(lǐng)域的實(shí)時(shí)計(jì)算技術(shù),主要還是Storm,算是比較成熟的一個(gè)技術(shù),另外就是Spark生態(tài)里的Spark Streaming。當(dāng)時(shí)可沒(méi)有什么現(xiàn)在較火的Flink、Druid等技術(shù)。
在仔細(xì)調(diào)研了一番過(guò)后發(fā)現(xiàn),根本沒(méi)有任何一個(gè)大數(shù)據(jù)領(lǐng)域的實(shí)時(shí)計(jì)算技術(shù)可以支撐這個(gè)需求。
因?yàn)镾torm是不支持SQL的,而且即使勉強(qiáng)你讓他支持了,他的SQL支持也會(huì)很弱,完全不可能運(yùn)行幾百行甚至上千行的復(fù)雜SQL在這種流式計(jì)算引擎上的執(zhí)行。
Spark Streaming也是同理,當(dāng)時(shí)功能還是比較弱小的,雖然可以支持簡(jiǎn)單SQL的執(zhí)行,但是完全無(wú)法支持這種復(fù)雜SQL的精準(zhǔn)運(yùn)算。
因此很不幸的是,在當(dāng)時(shí)的技術(shù)背景下,遇到的這個(gè)實(shí)時(shí)數(shù)據(jù)運(yùn)算的痛點(diǎn),沒(méi)有任何開(kāi)源的技術(shù)是可以解決的。必須得自己根據(jù)業(yè)務(wù)的具體場(chǎng)景,從0開(kāi)始定制開(kāi)發(fā)自己的一套數(shù)據(jù)平臺(tái)系統(tǒng)架構(gòu)。
八、分庫(kù)分表解決數(shù)據(jù)擴(kuò)容問(wèn)題
首先我們要先解決第一個(gè)痛點(diǎn),就是一旦單臺(tái)數(shù)據(jù)庫(kù)服務(wù)器無(wú)法存儲(chǔ)下當(dāng)日的數(shù)據(jù),該怎么辦?
第一個(gè)首選的方案當(dāng)然就是分庫(kù)分表了。我們需要將一個(gè)庫(kù)拆分為多庫(kù),不用的庫(kù)放在不同的數(shù)據(jù)庫(kù)服務(wù)器上,同時(shí)每個(gè)庫(kù)里放多張表。
采用這套分庫(kù)分表架構(gòu)之后,可以做到每個(gè)數(shù)據(jù)庫(kù)服務(wù)器放一部分的數(shù)據(jù),而且隨著數(shù)據(jù)量日益增長(zhǎng),可以不斷地增加更多的數(shù)據(jù)庫(kù)服務(wù)器來(lái)容納更多的數(shù)據(jù),做到按需擴(kuò)容。
同時(shí),每個(gè)庫(kù)里單表分為多表,這樣可以保證單表數(shù)據(jù)量不會(huì)太大,控制單表的數(shù)據(jù)量在幾百萬(wàn)的量級(jí),基本上性能優(yōu)化到極致的SQL語(yǔ)句跑起來(lái)效率還是不錯(cuò)的,秒級(jí)出結(jié)果是可以做到的。
同樣,給大家來(lái)一張圖,大家直觀的感受一下:
九、讀寫(xiě)分離降低數(shù)據(jù)庫(kù)服務(wù)器的負(fù)載
此時(shí)分庫(kù)分表之后,又面臨著另外一個(gè)問(wèn)題,就是現(xiàn)在如果對(duì)每個(gè)數(shù)據(jù)庫(kù)服務(wù)器又是寫(xiě)入又是讀取的話,會(huì)導(dǎo)致數(shù)據(jù)庫(kù)服務(wù)器的CPU負(fù)載和IO負(fù)載非常的高!
為什么這么說(shuō)呢?因?yàn)樵诖藭r(shí)寫(xiě)數(shù)據(jù)庫(kù)的每秒并發(fā)已經(jīng)達(dá)到幾千了,同時(shí)還頻繁的運(yùn)行那種超大SQL來(lái)查詢(xún)數(shù)據(jù),數(shù)據(jù)庫(kù)服務(wù)器的CPU運(yùn)算會(huì)極其的繁忙。
因此我們將MySQL做了讀寫(xiě)分離的部署,每個(gè)主數(shù)據(jù)庫(kù)服務(wù)器都掛了多個(gè)從數(shù)據(jù)庫(kù)服務(wù)器,寫(xiě)只能寫(xiě)入主庫(kù),查可以從從庫(kù)來(lái)查。
大家一起來(lái)看看下面這張圖:
十、自研的滑動(dòng)窗口動(dòng)態(tài)計(jì)算引擎
但是光是做到這一點(diǎn)還是不夠的,因?yàn)槠鋵?shí)在生產(chǎn)環(huán)境發(fā)現(xiàn),哪怕單表數(shù)據(jù)量限制在了幾百萬(wàn)的級(jí)別,你運(yùn)行幾百個(gè)幾百行復(fù)雜SQL,也要幾十秒甚至幾分鐘的時(shí)間,這個(gè)時(shí)效性對(duì)付費(fèi)級(jí)的產(chǎn)品已經(jīng)有點(diǎn)無(wú)法接受,產(chǎn)品提出的極致性能要求是,秒級(jí)!
因此對(duì)上述系統(tǒng)架構(gòu),我們?cè)俅巫隽思軜?gòu)的優(yōu)化,在數(shù)據(jù)平臺(tái)中嵌入了自己純自研的滑動(dòng)窗口計(jì)算引擎,核心思想如下:
- 在數(shù)據(jù)庫(kù)binlog采集中間件采集的過(guò)程中,要將數(shù)據(jù)的變更切割為一個(gè)一個(gè)的滑動(dòng)時(shí)間窗口,每個(gè)滑動(dòng)時(shí)間窗口為幾秒鐘,對(duì)每個(gè)窗口內(nèi)的數(shù)據(jù)打上那個(gè)窗口的標(biāo)簽
- 同時(shí)需要維護(hù)一份滑動(dòng)時(shí)間窗口的索引數(shù)據(jù),包括每個(gè)分片的數(shù)據(jù)在哪個(gè)窗口里,每個(gè)窗口的數(shù)據(jù)的一些具體的索引信息和狀態(tài)
- 接著數(shù)據(jù)平臺(tái)中的核心計(jì)算引擎,不再是每隔幾十秒就運(yùn)行大量SQL對(duì)當(dāng)天所有的數(shù)據(jù)全部計(jì)算一遍了,而是對(duì)一個(gè)接一個(gè)的滑動(dòng)時(shí)間窗口,根據(jù)窗口標(biāo)簽提取出那個(gè)窗口內(nèi)的數(shù)據(jù)進(jìn)行計(jì)算,計(jì)算的僅僅是最近一個(gè)滑動(dòng)時(shí)間窗口內(nèi)的數(shù)據(jù)
- 接著對(duì)這個(gè)滑動(dòng)時(shí)間窗口內(nèi)的數(shù)據(jù),可能最多就千條左右吧,運(yùn)行所有的復(fù)雜SQL計(jì)算出這個(gè)滑動(dòng)時(shí)間窗口內(nèi)的報(bào)表數(shù)據(jù),然后將這個(gè)窗口數(shù)據(jù)計(jì)算出的結(jié)果,與之前計(jì)算出來(lái)的其他窗口內(nèi)的計(jì)算結(jié)果進(jìn)行合并,最后放入MySQL中的報(bào)表內(nèi)
- 此外,這里需要考慮到一系列的生產(chǎn)級(jí)機(jī)制,包括滑動(dòng)時(shí)間窗口如果計(jì)算失敗怎么辦?如果一個(gè)滑動(dòng)時(shí)間窗口計(jì)算過(guò)慢怎么辦?滑動(dòng)窗口計(jì)算過(guò)程中系統(tǒng)宕機(jī)了如何在重啟之后自動(dòng)恢復(fù)計(jì)算?等等
通過(guò)這套滑動(dòng)窗口的計(jì)算引擎,我們直接將系統(tǒng)計(jì)算性能提升了幾十倍,基本上每個(gè)滑動(dòng)窗口的數(shù)據(jù)只要幾秒鐘就可以完成全部報(bào)表的計(jì)算,相當(dāng)于一下子把最終呈現(xiàn)給用戶(hù)的實(shí)時(shí)數(shù)據(jù)的時(shí)效性提升到了幾秒鐘,而不是幾十秒。
同樣,大家看看下面的圖。
十一、離線計(jì)算鏈路的性能優(yōu)化
實(shí)時(shí)計(jì)算鏈路的性能問(wèn)題通過(guò)自研滑動(dòng)窗口計(jì)算引擎來(lái)解決了,但是離線計(jì)算鏈路此時(shí)又出現(xiàn)了性能問(wèn)題。。。
因?yàn)槊刻炝璩繌臉I(yè)務(wù)庫(kù)中離線導(dǎo)入的是歷史全量數(shù)據(jù),接著需要在凌晨針對(duì)百億量級(jí)的全量數(shù)據(jù),運(yùn)行很多復(fù)雜的上千行復(fù)雜SQL來(lái)進(jìn)行運(yùn)算,當(dāng)數(shù)據(jù)量達(dá)到百億之后,這個(gè)過(guò)程耗時(shí)很長(zhǎng),有時(shí)候要從凌晨一直計(jì)算到上午。
關(guān)鍵問(wèn)題就在于,離線計(jì)算鏈路,每天都是導(dǎo)入全量數(shù)據(jù)來(lái)進(jìn)行計(jì)算,這就很坑了。
之所以這么做,是因?yàn)閺臉I(yè)務(wù)庫(kù)同步數(shù)據(jù)時(shí),每天都涉及到數(shù)據(jù)的更新操作,而hadoop里的數(shù)據(jù)是沒(méi)法跟業(yè)務(wù)庫(kù)那樣來(lái)進(jìn)行更新的,因此最開(kāi)始都是每天導(dǎo)入全量歷史數(shù)據(jù),作為一個(gè)最新快照來(lái)進(jìn)行全量計(jì)算。
在這里,我們對(duì)離線計(jì)算鏈路進(jìn)行了優(yōu)化,主要就是全量計(jì)算轉(zhuǎn)增量計(jì)算:每天數(shù)據(jù)在導(dǎo)入hadoop之后,都會(huì)針對(duì)數(shù)據(jù)的業(yè)務(wù)時(shí)間戳來(lái)分析和提取出來(lái)每天變更過(guò)的增量數(shù)據(jù),將這些增量數(shù)據(jù)放入獨(dú)立的增量數(shù)據(jù)表中。
同時(shí)需要根據(jù)具體的業(yè)務(wù)需求,自動(dòng)分析數(shù)據(jù)計(jì)算的基礎(chǔ)血緣關(guān)系,有可能增量數(shù)據(jù)需要與部分全量數(shù)據(jù)混合才能完成計(jì)算,此時(shí)可能會(huì)提取部分全量歷史數(shù)據(jù),合并完成計(jì)算。計(jì)算完成之后,將計(jì)算結(jié)果與歷史計(jì)算結(jié)果進(jìn)行合并。
在完成這個(gè)全量計(jì)算轉(zhuǎn)增量計(jì)算的過(guò)程之后,離線計(jì)算鏈路在凌晨基本上百億級(jí)別的數(shù)據(jù)量,只要對(duì)昨天的增量數(shù)據(jù)花費(fèi)一兩個(gè)小時(shí)完成計(jì)算之后,就可以完成離線計(jì)算的全部任務(wù),性能相較于全量計(jì)算提升至少十倍以上。
十二、階段性總結(jié)
到此為止,就是這套系統(tǒng)在最初一段時(shí)間做出來(lái)的一套架構(gòu),不算太復(fù)雜,還有很多缺陷,不完美,但是在當(dāng)時(shí)的業(yè)務(wù)背景下效果相當(dāng)?shù)牟诲e(cuò)。
在這套架構(gòu)對(duì)應(yīng)的早期業(yè)務(wù)背景下,每天新增數(shù)據(jù)大概是億級(jí)左右,但是分庫(kù)分表之后,單表數(shù)據(jù)量在百萬(wàn)級(jí)別,單臺(tái)數(shù)據(jù)庫(kù)服務(wù)器的高峰期寫(xiě)入壓力在2000/s,查詢(xún)壓力在100/s,數(shù)據(jù)庫(kù)集群承載的總高峰寫(xiě)入壓力在1萬(wàn)/s,查詢(xún)壓力在500/s,有需要還可以隨時(shí)擴(kuò)容更多的數(shù)據(jù)庫(kù)服務(wù)器,承載更多的數(shù)據(jù)量,更高的寫(xiě)入并發(fā)與查詢(xún)并發(fā)。
而且,因?yàn)樽隽俗x寫(xiě)分離,因此每個(gè)數(shù)據(jù)庫(kù)服務(wù)器的CPU負(fù)載和IO負(fù)載都不會(huì)在高峰期打滿,避免數(shù)據(jù)庫(kù)服務(wù)器的負(fù)載過(guò)高。
而基于滑動(dòng)時(shí)間窗口的自研計(jì)算引擎,可以保證當(dāng)天更新的實(shí)時(shí)數(shù)據(jù)主要幾秒鐘就可以完成一個(gè)微批次的計(jì)算,反饋到用戶(hù)看到的數(shù)據(jù)報(bào)表中。
同時(shí)這套引擎自行管理著計(jì)算的狀態(tài)與日志,如果出現(xiàn)某個(gè)窗口的計(jì)算失敗、系統(tǒng)宕機(jī)、計(jì)算超時(shí),等各種異常的情況,這個(gè)套引擎可以自動(dòng)重試與恢復(fù)。
此外,昨天以前的海量數(shù)據(jù)都是走Hadoop與Spark生態(tài)的離線存儲(chǔ)與計(jì)算。經(jīng)過(guò)性能優(yōu)化之后,每天凌晨花費(fèi)一兩個(gè)小時(shí),算好昨天以前所有的數(shù)據(jù)即可。
最后實(shí)時(shí)與離線的計(jì)算結(jié)果在同一個(gè)MySQL數(shù)據(jù)庫(kù)中融合,此時(shí)用戶(hù)如果對(duì)業(yè)務(wù)系統(tǒng)做出操作,實(shí)時(shí)數(shù)據(jù)報(bào)表在幾秒后就會(huì)刷新,如果要看昨天以前的數(shù)據(jù)可以隨時(shí)選擇時(shí)間范圍查看即可,暫時(shí)性是滿足了業(yè)務(wù)的需求。
早期的幾個(gè)月里,日增上億數(shù)據(jù),離線與實(shí)時(shí)兩條鏈路中的整體數(shù)據(jù)量級(jí)達(dá)到了百億級(jí)別,無(wú)論是存儲(chǔ)擴(kuò)容,還是高效計(jì)算,這套架構(gòu)基本是撐住了。
十三、下一階段的展望
這個(gè)大型系統(tǒng)架構(gòu)演進(jìn)實(shí)踐是一個(gè)系列的文章,將會(huì)包含很多篇文章,因?yàn)橐粋€(gè)大型的系統(tǒng)架構(gòu)演進(jìn)的過(guò)程,會(huì)持續(xù)很長(zhǎng)時(shí)間,做出很多次的架構(gòu)升級(jí)與重構(gòu),不斷的解決日益增長(zhǎng)的技術(shù)挑戰(zhàn),最終完美的抗住海量數(shù)據(jù)、高并發(fā)、高性能、高可用等場(chǎng)景。