快捷方式:
前面分析了在PostgreSQL和MySQL中進行多維數據查詢的挑戰。問題的根本在于,按行存儲的數據庫在行變得很大(wide table)的情況下,一旦索引無法完成所有的查詢工作,就會受到行大小的影響。為了避免按行存儲的缺陷,按列存儲的數據庫就被發明了出來。按行存儲的數據庫有很多,絕大部分都是要花錢的,開源的有MonetDB。和前面相同的數據量,相同的wide table的表設計,用MonetDB可以快上很多:
sql>select count(contact_id) from spike2 where a1 = 7;
+--------+
| L24 |
+========+
| 830569 |
+--------+
1 tuple (24.576ms)
sql>select count(contact_id) from spike2 where a1 = 7 and a2 = 5;
+--------+
| L25 |
+========+
| 164788 |
+--------+
1 tuple (105.352ms)
sql>select count(contact_id) from spike2 where a1 = 7 and a2 = 5 and a3 = 1;
+------+
| L26 |
+======+
| 4040 |
+------+
1 tuple (24.963ms)
從結果上來看,普遍是快上10倍,極端情況下要快上100倍。MonetDB之所以快,除了按行存儲之外,還有一個秘訣是它會被SQL編譯成小的關系代數操作。簡單的關系代數操作就比如加法,這個操作就只做兩個集合相加。每個“關系代數操作”都是用C甚至匯編優化的。普通的按行存儲的數據庫的實現其實是外層有個循環,循環體是一個解釋器,解析過的SQL和行的記錄是輸入。而MonetDB的編譯實現方式就避免了在循環里放置一個解釋器。這樣做的好處是有利于CPU做分支預測。因為解釋器的代碼相對于硬編碼的“關系代數操作”來說要復雜得多,相應的分支預測失敗率也高很多。
MonetDB的缺陷也是非常明顯的。每個關系代數操作都要把其結果完全計算出來(Materialized),也就是要占用內存。比如你要SELECT A+B+C,那么A+B的每行的結果就會被計算出來,然后其結果再與C相加。計算越復雜,中間計算結果需要贊用的內存就更多。寫MonetDB的教授們顯然也是預料到了這方面的問題,帶了個研究生研究如何把這個計算不用完全Materialized。研究結果就是一個叫X100的項目。寫這個項目的博士生后來去開了公司,名字叫VectorWise。后來這個公司又給Actian并購了。這個X100項目很牛,它在計算A+B+C的時候不是把A+C的所有行都計算了,然后才計算+C,而是分成幾kb的小段,把A+B+C連在一起計算。這樣做的好處是利用了CPU的緩存,A+B的計算結果還沒有從CPU的緩存中移出就被用來計算A+B+C了。

高級一些的按行存儲的數據庫都能比較好的處理內存不夠用,需要到磁盤的場景。MonetDB不行,它是完全基于內存映射文件的,一旦需要swap到磁盤,性能就慘不忍睹。

那么在完全使用內存的情況下,可以處理多少數據呢?可以大概估算一下一列的大小:
5904385 integer => 23m raw data
10518813 integer => 41m raw data
10518813 date time / bigint => 71m raw data
9531627 integer => 37m raw data
9531627 varchar(128) => 37m + 320m raw data (320m is the dictionary, it varies)
值得注意的是null也是要占空間的。
基于這樣的數據,在現在100多G內存的服務器上還是可以達到千萬級別的數據處理能力的。對于不太大的數據庫的近三個月交易數據還是能夠處理得過來的。
所以說,如果你要處理的數據量,不超過100GB,那么MonetDB還是值得一試的。要是數據量更大,要么選擇Vertica,VectorWise這樣的商業產品,要么就得自己做Sharding了。還有一個趨勢是,傳統的按行存儲的數據庫也在融合按列存儲的特性,出現了一些混合解決方案,比如PAX。總的一個來說,在這個領域,沒有完美的,特別成熟的開源解決方案,有可能一方面是因為有這樣需要的企業,都是有錢的主吧。另外一方面,普通的PostgreSQL和MySQL要是性能不是特別挑剔(本文限定的1s鐘限制其實并不實用,跑個分析任務怎么不得幾分鐘啊),其實還是堪用的。
浙公網安備 33010602011771號