改造mondrian的構想

     項目中最近需要使用mondrian,於是開始首先閱讀以下mondrian的源代碼,總體看來mondrian是一個ROLAP引擎,但是它的定位還是一個以庫形式 提供的組件,而不是作爲服務,例如它將所有的緩存都放在進程內部,當我在一個進程中創建多個cube並且一直保持着他們的connection會導致OOM,所以對mondrian作爲服務化的首要改造還是在於如何將mondrian的緩存挪出本進程,放到第三方的存儲系統例如redis、hbase等NOSQL存儲系統中,這樣一方面可以解決上面提到的進程內緩存問題,另一方面可以將數據獨立出去可以將服務器做成無狀態的,可以水平擴展,保證高可用性。除了緩存這一步之外,另外需要對mondrian做優化的地方還有生成SQL這一部分,mondrian生成的SQL主要包括查看一個level下所有的member和查看一個cell結果這兩類,前者無非就是select distinct xxx或者select xxx group by xxx,mondrian一般使用select distinct的方式,這種沒有什麼可以優化的地方,另外還有一種查詢是查詢多個維度聚合之後的某一個度量的值,一般是多個維度group by之後計算某一列的sum、avg等值,但是如果在group by之前需要過濾掉一些元素的話,mondrian會將剩餘的語句翻譯成where xxx IN (xxx1, xxx2,...)(通過執行日誌可以看到),這種SQL執行效率是比較低的,即使xxx這一列有主鍵,改造的方案是儘可能得將IN語句改成多個between或者大於小於這樣的SQL,這個的優先級可以低一些。

     上面提到的是對mondrian本身不足需要進行改造的地方,但是像mondrian這樣的一個ROLAP系統的執行性能的確不怎麼讓人滿意,業界做的一些OLAP引擎(例如百度的palo、ebay的kylin等)總是生成可以再查詢速度秒級,而mondrian的緩存很渣,每次如果再去數據源執行SQL又會很慢,因此我們對這個性能還需要特別注意,polo自己做了數據倉庫,所有需要查詢的數據都由該系統自己維護,因此做性能優化也是很容易的,而mondrian使用的數據在數據源(關係型數據庫或者大數據系統中)存儲,我們並不能改變它存儲的形式,因此大部分的性能還是取決於數據源的性能(是否加主鍵,是否使用SSD存儲,hadoop集羣節點數等等),基於這個限制我覺得如果想要對性能有質的提升也只有一條路,緩存,緩存,緩存,重要的事情說三遍,但是畢竟緩存一個cube的緩存量還是太大了(下面粗略的計算一下一個cube需要的緩存量),其實如果我們有一個足夠大的緩存,我們理論上也就是自己做了數據倉庫,這方面還是可以考慮的,既然自己可以使用緩存作爲數據倉庫,那麼就需要做類似ETL操作,預先將用戶的數據加載到緩存中,也就是所謂的cube預計算,但是和真正的數據倉庫不一樣的地方在於,數據倉庫存儲的全局的需要分析的數據,而預計算的只是針對一個cube所能夠使用的數據,目前來看,Kylin的預計算方式是值得我們學習的,但是Kylin主要解決的問題還是基於hadoop系統下的數據分析問題(數據使用hadoop存儲,使用hive查詢的),由於hive的速度實在是讓人難以接受(尤其是負載的OLAP查詢產生的SQL),因此做預計算並且將結果緩存到hbase是非常有必要的,但是既然我們使用mondrian這樣的引擎就是希望我們的數據源既可以是關係數據庫還可以是hive之類的數據庫,甚至使用hbase(phoenix提供SQL接口),這裏的預計算對於關係數據庫是否必要就值得考慮一下了。

     既然做了預計算,也就是做緩存,無法避免的問題就是緩存一致性問題了,如何保證在數據量更新的情況下cube的緩存數據也能更新到最新的狀態,當然對於OLAP系統來說數據的實時性不是最重要的,一般情況下幾個小時更新一次數據或者一天更新一次數據也是可以接受的,而更新數據源之後也不要求cube的數據能夠立即更新,能夠達到最終一致性的要求也是可以接受的。但是當數據源導入新的數據的時候可能導致整個cube的值都發生改變,如果快速的計算新的cell值也是需要解決的問題,當前看到的一些系統都是用了倒排索引(還不瞭解)的方式來實現增量計算,具體的實現可以參考一下Kylin的代碼。

     接下來的任務和方向主要是熟悉mondrian和kylin兩個系統的實現,能夠基於他們實現出我們自己的面向mdx查詢和sql查詢的OLAP引擎(目前主要的方向還是mdx),但是mondrian對於mdx的支持還是有一定的缺陷的,最主要的問題就是它目前不支持子查詢(難道考慮到中間結果過大問題),因此一些高級的過濾操作目前還是實現不了,例如取出按照年份聚合之後總銷量小於1W的所有年份,然後對過濾之後的數據按照其他維度進行mdx查詢,這其實相當於對數據源進行一定的過濾,首先使用mdx查出需要過濾的年份,然後在數據源中刪除所有年份等於這些的數據(相當於每次查詢在源事實表的基礎上加了一個where條件),如果在外層實現這樣的過濾又太過麻煩,因此對於這樣的功能性的缺失目前還不知道如何解決。另外mondrian在定義事實表的時候不僅僅可以支持指定一個table,還可以指定任意的視圖,這也就以爲這任意的SQL語句的結果都可以作爲事實表,使用這種方式可以滿足上面的需求但是對於每次查詢翻譯成的SQL都需要多層的子查詢,性能可想而知了。後期我們如果能夠緩存整個cube的時候可以直接才cube的基礎上滿足這樣的需求。

     另外,mondrian裏面的維度是具有層級關係的,當我們定義了一個月級別,它的父級別是年,那麼月級別的成員需要攜帶上具體的年份,這時候如果只按照月份進行聚合統計的時候需要得到的結果是[1997].[1],[1998].[1]這樣的值,而不是[1],[2]這樣的結果,如果實現這樣的結果還需要將每一個維度的層級進行拆分,這樣無疑是非常複雜的,不知道mdx裏面有沒有什麼關鍵字可以在指定一個level的是打破層架關係。

     接下來就要深入看一下mondrian的代碼了,其實mondrian的主要接口也就兩個,首先是創建connection,其中包括加載cube的過程,另外就是執行一次mdx查詢,查詢的流程相對比較複雜,涉及到緩存以及如何生成SQL,另外對於mondrian當前緩存的結構以及對緩存的管理也需要重點關注。

     最後粗略的算一下一個cube的大小,假設場景是這樣的一個星型結構,包括一個事實表和3個維度表,分別是時間,地區,產品信息,其中時間維度分爲三個層級,分別是年份,季度和月份,假設年份有10個成員,季度有4*10個成員,月份有10*4*3個成員,地區有三個級別,分別是國家,省份和城市三個級別,國家有100個成員,省份一共有1000個成員,城市有5000個成員;產品維度包含兩個層級,分別爲產品分類和產品商標,前者有16個成員,後者一共有500個成員,那麼整個cube就是所有維度集合可能的聚合值,其中每一個維度包含一個特殊的成員ALL成員,這個成員屬於一個特殊的層級ALL層級(按照mondrian的思想),每一個維度下的ALL層級是最高的層級,時間維度的深度爲4,地區維度的深度爲4,產品維度的深度爲3,那麼我們從最底層的層級進行組合,一共包含時間維度的最底層級別包含120個成員,最底層包含5000個成員,產品維度最底層級別包含500個成員,那麼可能的組合值就是120*5000*500=3億個組合元素,這是最底層的組合元素,這些所有的組合可以看成一個長爲120,寬爲500,高爲5000的立方體,這3億個元素就是整個立方體,這個立方體中每一個單元裏面包含每一個組合(月=xxx,城市=xxx和產品商標=xxx)的聚合值,一個cell包含所有的度量值,這樣整個立方體就建立起來了,這個立方體是全量的值,其他的組合值可以都可以通過將該立方體的某一個子立方作爲一個cell進行計算,例如我們要計算年份爲1997,國家爲中國,產品類型爲食品的銷售總額,那麼就相當於將1997年下的所有月份(12個)、中國的所有城市(假設100個)和產品類型爲食品的所有商標(假設爲50個)所有組合的聚合值,也就是12*100*50=60W個 cell進行組合成的新的cell作爲返回的結果。如果我們想要緩存整個cube,最好的情況下我們還是需要緩存所有最低級別所有成員的組合(因爲從高層級得不到低層級的信息,除非從數據源獲取),這個代價還是相當大的,一般情況下我們需要對成員進行建索引(爲每一個成員制定下標),然後通過下標的組合作爲key,度量值 的組合作爲value進行緩存,但是當數據源哪怕插入一條數據都會改變整個cube中的大量的cell值,這個增量計算還是相當可怕的。

     在不考慮增量計算的情況下,其實這個還是緩存量和查詢速度的博弈,如果緩存量不夠,勢必會緩存一些高層級,這樣對於底層級的查詢就需要走數據源,性能就差,如果查詢緩存底層級成員的組合那麼所有的查詢都不需要再走數據源,而是直接在內存中計算。當然最好的辦法還是需要判斷一下最常用的層級和每一個層級的成員個數,如果某一層級的成員個數過多則不適合緩存,如果查詢頻率比較高的層級則更適合緩存。

     任務艱鉅啊,還是先一步一個腳印的走吧,首先第一步將mondrian的緩存移出程序並且考慮一下緩存結構是否有比較再進行優化,第二步可以分析一下mondrian的執行SQL,看一下有沒有優化的空間,第三步是進行預計算保存在緩存中,當然這時候暫時不考慮到動態的增量更新,最後再考慮如何做到增量計算。
發佈了70 篇原創文章 · 獲贊 131 · 訪問量 55萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章