第1期:多維分析的后臺(tái)性能優(yōu)化手段
多維分析就是針對(duì)一個(gè)事先準(zhǔn)備好的數(shù)據(jù)立方體實(shí)施旋轉(zhuǎn)、切片(切塊)、鉆取等交互操作的過(guò)程,經(jīng)常也被直接稱(chēng)為OLAP。它的后臺(tái)運(yùn)算在結(jié)構(gòu)上很簡(jiǎn)單,如果用SQL語(yǔ)法描述,大體形式為:
SELECT D,…, SUM(M), … FROM C WHERE D’=d’ AND … GROUP BY D,…
即對(duì)立方體按某些維度分組匯總某些測(cè)度。其中C是數(shù)據(jù)立方體,D,…是選出維度,M,…是聚合測(cè)度,聚合函數(shù)也可以不是SUM。D’是切片維度,切塊時(shí)條件為D IN (d,…),WHERE中還可以增加針對(duì)某些測(cè)度的條件,一般也就是選出某個(gè)區(qū)間內(nèi)的值。
多維分析就是針對(duì)一個(gè)事先準(zhǔn)備好的數(shù)據(jù)立方體實(shí)施旋轉(zhuǎn)、切片(切塊)、鉆取等交互操作的過(guò)程,經(jīng)常也被直接稱(chēng)為OLAP。它的后臺(tái)運(yùn)算在結(jié)構(gòu)上很簡(jiǎn)單,如果用SQL語(yǔ)法描述,大體形式為:
SELECT D,…, SUM(M), … FROM C WHERE D’=d’ AND … GROUP BY D,…
即對(duì)立方體按某些維度分組匯總某些測(cè)度。其中C是數(shù)據(jù)立方體,D,…是選出維度,M,…是聚合測(cè)度,聚合函數(shù)也可以不是SUM。D’是切片維度,切塊時(shí)條件為D IN (d,…),WHERE中還可以增加針對(duì)某些測(cè)度的條件,一般也就是選出某個(gè)區(qū)間內(nèi)的值。
OLAP需要即時(shí)響應(yīng),對(duì)性能要求很高,而這個(gè)運(yùn)算形式雖然很簡(jiǎn)單,但數(shù)據(jù)量大時(shí)的計(jì)算量也不小,如果不設(shè)法優(yōu)化,效率就可能很差。下面我們介紹多維分析后臺(tái)建設(shè)時(shí)幾種經(jīng)常被采用的性能優(yōu)化手段。
預(yù)先匯總
預(yù)先匯總是早期OLAP產(chǎn)品常用的手段,簡(jiǎn)單地就是拿空間換時(shí)間。把部分或者全部維度組合(GROUP BY子句)的匯總值(SELECT中的聚合測(cè)度)先計(jì)算出來(lái)保存,以后的計(jì)算可以直接取出或從這些中間結(jié)果再計(jì)算,性能會(huì)好很多。
預(yù)先匯總占用的空間有點(diǎn)大。如果保存全部維度組合,一般應(yīng)用場(chǎng)景下(十幾到幾十個(gè)維度,維度取值范圍在幾到幾十之間),簡(jiǎn)單計(jì)算可知,空間占用會(huì)比原始立方體大數(shù)倍到數(shù)十倍((k1+1)*(k2+1)*…與k1*k2*…之間的比,還要考慮多種聚合函數(shù))。雖然要保證即時(shí)響應(yīng)時(shí)立方體都不會(huì)太大,但再大幾十倍經(jīng)常也還是難以接受的。
折衷辦法是只保存部分維度組合。OLAP過(guò)程中在界面上呈現(xiàn)出來(lái)的分組維度(GROUP BY子句)不會(huì)太多,可以只匯總所有m個(gè)維度的組合,在m不太大時(shí)(一般不超過(guò)5),空間增長(zhǎng)還可以容忍,而用戶(hù)的大多數(shù)操作都可以得到較迅速響應(yīng)。
麻煩在于,部分匯總解決不了針對(duì)其它維度的切片條件,鉆取動(dòng)作就是以切片為基礎(chǔ)的。而且,即使全量匯總也無(wú)法處理測(cè)度上的條件(比如銷(xiāo)售額超過(guò)1000元的統(tǒng)計(jì)),而多維分析時(shí)常常允許這些動(dòng)作,甚至聚合函數(shù)也可能帶有條件(只合計(jì)100元以下的費(fèi)用),這些都無(wú)法使用預(yù)先匯總的結(jié)果。
預(yù)先匯總只能解決小部分最常見(jiàn)的計(jì)算,更多的情況還是要靠硬遍歷。
分段并行
多維分析本質(zhì)上是過(guò)濾和分組匯總,這種運(yùn)算很容易并行。只要簡(jiǎn)單地?cái)?shù)據(jù)拆成多段后分別處理,收集到結(jié)果再匯總。各個(gè)子任務(wù)之間沒(méi)有依賴(lài)關(guān)系,無(wú)論是單機(jī)多線(xiàn)程還是集群多機(jī)或者綜合有之,都不難實(shí)現(xiàn)。
多維分析的結(jié)果是要呈現(xiàn)給人看的,而人可以觀察的數(shù)據(jù)量遠(yuǎn)遠(yuǎn)小于現(xiàn)代計(jì)算機(jī)的內(nèi)存??梢苑湃雰?nèi)存的小結(jié)果集不需要和外存交換,程序設(shè)計(jì)復(fù)雜度較低,運(yùn)算性能也好。如果運(yùn)算時(shí)發(fā)現(xiàn)結(jié)果集太大是可以直接報(bào)告給界面相應(yīng)信息并中止。
實(shí)踐測(cè)試表明:多線(xiàn)程計(jì)算時(shí),不要采用各子任務(wù)向同一個(gè)結(jié)果集匯總的方案,這樣看起來(lái)會(huì)減少內(nèi)存占用(各子任務(wù)共用一個(gè)最終結(jié)果集),但多線(xiàn)程搶占同一資源需要的同步動(dòng)作會(huì)嚴(yán)重影響性能。
線(xiàn)程數(shù)也不是越多越好,顯然超過(guò)CPU核數(shù)就沒(méi)有意義了。如果數(shù)據(jù)在外存,還要考慮硬盤(pán)的并發(fā)能力,一般會(huì)比CPU核數(shù)小很多,具體合適的數(shù)值需要實(shí)際測(cè)試才知道。
在數(shù)據(jù)不再變化時(shí)分段也容易,按記錄數(shù)切分后設(shè)置分段點(diǎn)即可。數(shù)據(jù)可追加時(shí)要做到較平均的分段會(huì)有些麻煩,以后再另外撰文陳述。
對(duì)于單個(gè)計(jì)算任務(wù),并行后常常有數(shù)倍的性能提升。但是,OLAP操作本身就是個(gè)并發(fā)性事務(wù),即使用戶(hù)數(shù)不大,也足以抵消并行計(jì)算帶來(lái)的性能提升。
還要再想辦法。
排序索引
沒(méi)有切片的匯總運(yùn)算總是要涉及全量數(shù)據(jù),如果不是預(yù)先匯總,也沒(méi)什么辦法再減少計(jì)算量了。但有切片運(yùn)算時(shí)(鉆取動(dòng)作),如果數(shù)據(jù)能合理組織,就未必要遍歷所有數(shù)據(jù)了。
如果我們?yōu)榫S度D建立索引(即把各記錄的D值及記錄位置按D值排序),那么涉及D的切片條件就可以迅速定位到相應(yīng)的記錄上(簡(jiǎn)單二分法),不需要遍歷全量數(shù)據(jù),計(jì)算量常常會(huì)有數(shù)量級(jí)的減少(取決于D的取值范圍)。理論上我們可以為每個(gè)維度都建立索引,這個(gè)成本并不算高,這樣只要涉及有切片時(shí),性能就會(huì)大幅提升。
需要指明的是,為多個(gè)維度D1,D2建立的多字段索引用處并不大,它不能用于迅速定位只有D2的切片,只能用于對(duì)D1,D2都有切片條件的情況。在選擇取值范圍***的那個(gè)切片維度用于定位后,計(jì)算量減少已經(jīng)很多了,其它維度的切片可以仍用遍歷手段。
不幸的是,這種原始方案只適用于可以頻繁小量訪(fǎng)問(wèn)的內(nèi)存數(shù)據(jù)。如果數(shù)據(jù)量大到必須放在外存中(而這是經(jīng)常發(fā)生的),按索引大量取出實(shí)際上并未連續(xù)存儲(chǔ)的數(shù)據(jù)時(shí),性能并不會(huì)有明顯提高。外存數(shù)據(jù)必須被真實(shí)排序、保證相應(yīng)切片的數(shù)據(jù)是連續(xù)存儲(chǔ)的,性能提升才會(huì)有效。
如果對(duì)每個(gè)維度都做排序,那相當(dāng)于數(shù)據(jù)要被復(fù)制若干倍,這個(gè)成本就有點(diǎn)高了。
一個(gè)折衷的辦法是把做兩個(gè),按維度D1,…,Dn排序一次,再按Dn,…,D1排序一次,數(shù)據(jù)量只是翻倍,還能容忍。總能找到一個(gè)切片維度在兩個(gè)維度排序列的前半部分,這樣該維度切片的數(shù)據(jù)還是基本連續(xù)的,性能提升仍會(huì)較為明顯。
列存壓縮
對(duì)付多維分析還有個(gè)大殺器:列式存儲(chǔ)。
多維分析的立方體中字段(維度和測(cè)度)常常都很多,幾十個(gè)上百個(gè)都很正常,但同時(shí)需要取用的字段并不多,如果不算切片維度,通常也就5個(gè)左右或更少。而切片可以用上面的索引方案解決,實(shí)際要遍歷的字段也仍然不多。
這時(shí)候列存就會(huì)有巨大優(yōu)勢(shì)了。外存計(jì)算的IO時(shí)間占比相當(dāng)大,減少數(shù)據(jù)讀取量比減少運(yùn)算量常常能更有效地提高性能。一個(gè)100個(gè)字段的立方體,如果只取5個(gè)字段時(shí),IO開(kāi)銷(xiāo)只有1/20,這會(huì)帶來(lái)數(shù)量級(jí)的性能提升。
列存還有個(gè)優(yōu)勢(shì)是可以壓縮數(shù)據(jù)量。如果按前述所說(shuō)將數(shù)據(jù)按維度D1,…,Dn排序存儲(chǔ),我們會(huì)發(fā)現(xiàn)D1在連續(xù)許多記錄中取值都相同,D2也是類(lèi)似,但程度會(huì)弱一些,越往后的維度連續(xù)相同的程度越弱,Dn就會(huì)幾乎沒(méi)有相同連續(xù)值。連續(xù)相同的值沒(méi)必要重復(fù)存儲(chǔ),可以只存一次并記錄個(gè)數(shù),這樣將可以進(jìn)一步減少存儲(chǔ)量,也就是減少外存IO訪(fǎng)問(wèn)量,從而提高性能。
當(dāng)然,列存也并不全是好處。
因?yàn)椴粶p少計(jì)算量,列存對(duì)于內(nèi)存數(shù)據(jù)用處不大。不過(guò)壓縮存儲(chǔ)方式仍然有意義,可以減少內(nèi)存占用。
使用列存會(huì)使分段并行及建立索引的處理變得更復(fù)雜,各個(gè)列需要同步分段才能并行處理,索引也需要同步指向所有列,而使用壓縮機(jī)制后同步更為麻煩。不過(guò),總得來(lái)講,在數(shù)據(jù)已經(jīng)確定不再變化時(shí),雖然麻煩,但難度并不算大,只是別忘處理了就行。
列存還會(huì)加大硬盤(pán)的并發(fā)壓力,在總字段數(shù)不多或取用字段較多時(shí)并沒(méi)有優(yōu)勢(shì)。對(duì)于機(jī)械硬盤(pán),如果再使用并行手段進(jìn)一步加劇并發(fā)壓力,很可能導(dǎo)致性能不升反降的結(jié)果,對(duì)于易于并發(fā)的固態(tài)硬盤(pán)使用列存較為合適。