MonetDB列存資料庫架構初探

大樹mu發表於2018-03-21

前言

對於越來越多的分析型場景,例如資料倉儲,科學計算等, 經典的資料庫DBMS的檢索效能頗顯乏力。
相反的,最近出現了很多面向列存的資料庫DBMS,像ClickHouse,Vertica, MonetDB等,因其充分利用了現代計算機的一些硬體優勢,同時捨棄了一些DBMS特性,得到了非常好的檢索效能。本文就MonetDB, 整理一些資料和程式碼,簡單介紹其核心設計。 通過簡單Benchmark, 驗證其檢索效能。

飛速發展的硬體

CPU計算模型

在過去的幾十年中,CPU的架構設計經歷了巨大的發展和變化,如今的CPU結構已經變動的非常複雜和精巧,其計算能力也得到了顯著提升。新硬體的發展驅使應用軟體不斷改進其架構設計,以至於更加貼合新硬體的優勢,使應用軟體表現出更好的效能。

下圖展示了基礎CPU模型和Pentium 4 CPU的結構(雖然Pentium也已經過時了),為了簡化對CPU計算過程的認識,從基礎CPU模型開始:

image.png

  • Data :
    CPU 計算運算元,即每次計算的輸入資料,例如一個整型值;
  • Code :
    CPU 計算指令,命令CPU做何種計算;
  • Storage :
    儲存運算元和指令的容器,通常為主存RAM;
  • Registers:
    暫存器,CPU內部儲存單元,儲存CPU當前執行的指令需要操作的資料。
  • Execution Unit :
    計算執行單元,不斷讀取下一個待執行的指令,同時從暫存器讀取該指令要操作的資料,執行指令描述的計算,並將結果寫回暫存器。

以上這些抽象的邏輯單元都比較簡單易理解。 該模型下CPU的每一次Cycle只執行一條指令。通常情況下,每條指令的執行又可以分解為以下4個嚴格序列的邏輯Stage :

  • Instruction Fetch(IF) : 指令獲取。獲取待執行的指令。
  • Instruction Decode(ID) : 指令解碼。根據系統指令集解碼指令為microcodes。
  • Execute(EX) : 執行指令。執行解碼後的microcodes, 不同型別的計算過程通常需要不同的物理計算單元來完成,例如代數計算單元,浮點數計算單元,運算元載入單元等。
  • Write-Back(WB) : 儲存計算結果到暫存器。

不同的Stage通常由不同的CPU物理單元來完成,由於這幾個Stage必須以嚴格序列的方式執行,所以最簡單的執行方式就是:每個CPU物理單元完成自己負責的Stage任務後將結果作為執行下一個Stage的CPU物理單元的輸入,弊端就是,例如執行`EX`的CPU 物理單元必須等待執行`ID`的CPU物理單元完成,在此之前,它都是空閒的, 如下圖”Sequential execution”所示。 為了提高效率,採用了pipeline 的模式,如下圖”Pipelined execution”所示:
image.png

儘管Pipelined的執行方式並沒有改變單條指令的執行時間,但整體指令流的執行吞吐明顯提升了。如上圖所示,吞吐提升了4倍。當然,這僅是理想情況。

為了進一步提高CPU指令計算的吞吐,現代CPU進一步發展了Superscalar的執行方式,通過內建多個同型別的物理執行單元,來獲取更”寬”的pipeline。 例如,在只有一個代數運算單元的CPU中,代數運算的pipeline寬度為1,即每個CPU Cycle只有一條代數運算指令被執行。如果把代數運算單元double一倍,那麼一個CPU Cycle內就可以有兩條代數運算指令在不同的代數運算單元上同時執行,IF Stage也可以一次性獲取兩條指令作為pipeline的輸入,此時Pipeline的寬度為2,CPU指令計算的吞吐又提升了一倍。

另外,除了通過擴充套件CPU單元的數量來提升CPU計算的吞吐,還可以通過提升單個CPU運算單元的能力來達到目的。 以前,一個代數運算單元一次(在一個CPU Cycle內)對一份運算元,執行一個代數運算指令,得到一份結果,這稱為單指令單資料流(SISD)。 如現在的大多數CPU的代數運算單元,可以一次對多份運算元,執行同一個運算指令,得到多份結果,這稱為單指令多資料流(SIMD)。假設在某些場景下,如果都是相同型別的計算,只改變計算的引數(例如影像領域,3D渲染等), 利用SIMD又可以成倍提升CPU計算吞吐。

Pipelined Execution, 其對CPU計算吞吐的提升都僅限於理想情況,其基本的假設是CPU正確的推測了馬上會執行的指令並將其加入Pipeline。但是很多情況下,下一個要執行的指令可能必須基於執行時的判斷。如果下一個指令恰好跳出了已經Pipelined的指令的範圍,那CPU不僅要等待載入該指令,而且當前整條Pipeline需要被清空並重新載入,這被稱之為Control Hazards, 另外,當跳轉發生時,不僅指令需要被載入,指令相關的運算元也需要被重新獲取,如果Cache-Miss導致主存Lookup,則至少要浪費上百個CPU Cycle, 這稱之為Data Hazards。 這類因指令跳轉造成吞吐下降的現象稱為Pipeline-bubble。

現代CPU架構中,CPU-Cycle的Stage劃分更加精細,加深了Pipeline的深度。更深的Pipeline有利於提高CPU計算的吞吐,但Pipeline-bubble的問題也被進一步放大。因此,對CPU-Friendly的程式碼,其效能更好,反之,效能反而有所下降。

拋開上面的邏輯模型,巨集觀上,現代CPU大都採用同步多執行緒技術(SMT), 多處理器架構(CMP)等,整體提升CPU的平行計算能力。

記憶體系統

相比於CPU計算能力的巨幅提升,Memory Access的時間並不顯著。因此,在現代主流計算機中,一個memory-access指令需要等很長的時間(幾百個CPU-Cycle)才能把資料載入到暫存器中。

為了緩解此類問題,記憶體系統被設計為多層次記憶體體系。一些更快,但容量較小的靜態儲存媒介被加到記憶體體系中,作為主存RAM的快取Cache Memory。Cache Memory分為D-Cache 和I-Cache, 分別用於儲存資料和指令(程式碼);

在該體系中,按data access的時間從小到大,依次有Register, L1 Cache, L2 Cache, L3 Cache, 主存RAM等(相應的data access時間可以參考Jeff Dean`s Number)。

Cache Memory 通常被按固定大小分為很多個Cache lines, 每個Cache line快取主存中的某一塊相應大小資料。Memory access 被設計為先訪問Cache Memory,如果恰好訪問地址在Cache lines中,稱為cache-hit, 反之cache-miss。 cache-miss 引發主存lookup,並換入lookup到的資料塊到cache line中。因為虛擬檔案系統的關係,cache-miss時,lookup主存有可能引發page-fault, 延長資料獲取時間。

更多關於Memory的知識可以閱讀:What every programmer should know about memory.

資料庫Database: 行or列

經典關聯式資料庫在邏輯上把資料儲存成一系列行(row)的集合,每行對應關係資料的一個Tuple。在物理儲存上,也是如此。這種儲存方式稱之為NSM(N-ary storage model)。 與之相應的,是一種按列組織並儲存資料的方式,稱之為DSM(Decomposed storage model), 如下圖所示:

image.png

通常把這兩種不同儲存形式的資料庫稱為行存資料庫或列存資料庫。列存資料庫形式出現的也很早,只是在當時硬體條件下,列存資料庫只有在select很少列的情況下,效能才會稍好於各類行存資料庫。這也形成了現在很多人固有的觀念:”如果查詢很多列,行存資料庫完勝列存資料庫;”。加之以前資料庫覆蓋的都是OLTP場景,使用者存取資料遵守三正規化原則,只要讀取一兩個列的情形也比較少。

但隨著硬體技術發展,列存資料庫的能力又需要被重新審視。下圖列舉了到2010年為止,已知的一些列存資料庫讀取多列資料效能的增長, 橫軸為查詢列佔該關係所有列的比例。

image.png

可以看到,近年來,即使是SELECT所有列資料,列存資料庫的效能也不輸行存資料庫。尤其在一些資料倉儲或OLAP的場合,資料組織不在遵守三正規化,而是為了節省關聯時間儲存成大寬表。反而SELECT部分列更貼合需求。

經典行存資料庫的效能分析

理論分析

除了資料儲存方式的異同,查詢執行層的設計也可能略有差異。經典關聯式資料庫基於一個稱之為Tuple-at-a-time的迭代模型,各種關係運算元按照一定的拓撲結構串聯成查詢計劃。查詢執行時,資料按照運算元之間拓撲關係構成pipeline。 如下圖右側所示,查詢語句為:

SELECT Id,Name,Age,(Age-30) * 50 as Bonus FROM People WHERE Age > 30;

image.png

圖中,子運算元(位於上層的運算元)呼叫next()函式向父運算元(位於下層的運算元)請求資料,最底層父運算元從儲存層獲取資料,處理後返回給子運算元,以此類推,每次next請求只返回一條資料(Tuple)。整個執行模型是基於子運算元的Pull模型,也稱之為‘Pull based iterator model’。

Tuple-at-a-time迭代模型下,每個Tuple從儲存層讀取出來後,都要經歷很多個函式呼叫。例如,每個父子運算元之間的next()函式,Select運算元呼叫filter函式,Project運算元要呼叫代數運算函式等等。整個執行過程中,很多函式,程式碼,狀態被重複很多次的頻繁讀取,呼叫,更新。從而導致了一些效能缺陷。有學者從理論上分析了一下一些效能問題:

CPU Instruction Cache : 由於查詢計劃可能包含很多不同型別的運算元,其組合到一起的所有指令的大小可能超過了I-Cache的容量。 每處理一個Tuple,CPU都需要在不同的運算元之間不斷的切換上下文(包括運算元的計算指令,狀態資料等),如果I-Cache不足以容納所有指令,cache-miss會在整個執行過程頻繁發生。

Function call overhead : 函式呼叫本身需要額外的CPU-Cycle, 尤其是當引數很多的時候。另外,如果是dynamically-dispatched的函式,可能需要幾十個CPU-Cycle(因為跳轉導致了Pipeline bubble)。

Tuple manipulation: 每個Tuple都包含多個屬性, 對幾個屬性的操作(例如Select Age > 30)需要先定位並讀取出Age屬性,這種對Tuple的解釋在每個Tuple處理時都要被重複很多次,花費很多額外的指令。

Superscalar CPUs utilization: 由於在Tuple-at-a-time模型中,每個運算元一次只處理一個Tuple, 因此無法有效利用現代CPU Superscalar的特性。從而對現代CPU而言,每個CPU-Cycle, 只能執行很少的指令(low instructions-per-cycle)。

data volume : 由於按行儲存的原因,計算中每個Tuple所有屬性都被完整的讀取出來,從磁碟到主存,到Cache。如果計算只需要部分屬性,即浪費了RAM和Cache的空間,也浪費了Disk->RAM->Cache的頻寬。

實驗分析

也有學者Trace TCP-H Query 1的執行,從實驗角度證明經典關聯式資料庫在CPU利用率的低效。

# TPC-H Query 1 
SELECT returnflag, sum(extprice * 1.19) as sum_vat_price FROM  lineitem WHERE shipdate <= date ‘1998-09-02’ GROUP BY returnflag. 

image.png

上圖Benchmark, 查詢執行過程中所有的函式呼叫按照其被呼叫的先後順序排序,第一列為表示查詢總時間,第二列表示當前Function執行耗時。第三列表示Function被呼叫執行的總次數,第四第五列表示每次Function執行所需的平均指令數以及平均IPC(Instruction-per-cycle)。

Benchmark至少說明了,在Tuple-at-a-time的經典行存資料庫查詢中,使用者定義的資料計算的函式(+,-,*,sum,avg),其執行時間佔查詢總時間比例約10%。28%的時間耗費在聚合操作時,對Hash-Table的查詢,寫入。而62%的時間耗費在像rec_get_nth_field這樣函式執行上,他們的主要功能,就是解釋Tuple, 例如定位,讀取或寫入Tuple中的某些屬性。如此來看,對於每一個Tuple, 需要被執行的指令(instructions-per-tuple)非常多。 另一方面,所有函式執行的平均IPC約為0.7, 極大的低於現代Superscalar 處理器的計算水平。

總結起來,在經典關聯式資料庫中,一個很高的instructions-per-tuple, 和一個很低的instructions-per-cycle, 導致了查詢時很高的cycles-per-tuple。也就是說,即使是很簡單的計算操作,每個Tuple的處理也很可能需要幾百個CPU Cycle。 CPU的計算能力沒有被充分利用,資料庫查詢的效能還有很大的提升空間。 正是為了解決這些問題,才有了MonetDB這些高效能列存資料庫DBMS。

MonetDB

也許正是因為現有的資料庫系統都沒有充分利用現代計算器巨大潛力,才有了一些列像MonetDB這些高效能資料庫的出現。MonetDB被設計為面向資料密集型應用,使用新的查詢模型,更高效的處理資料。MonetDB裡提出的所有資料處理技術都圍繞兩個點:一是面向CPU-Efficient計算, 二是儲存頻寬優化。

儲存模型

區別於經典關聯式資料庫,MonetDB採用DSM模型作為資料儲存格式。Tuple的各個屬性被分開儲存,相同屬性的值被儲存在一起,即典型的按列儲存方式。邏輯上,MonetDB中每一列被儲存為一個Binary Association Table(BAT), 簡單來說,就是一個只有兩列的表,第一列儲存虛擬ID,第而列儲存屬性值。同一個Tuple的不同屬性具有相同的虛擬ID, 即可以通過相同虛擬ID來關聯同一Tuple的不同屬性,從而構建Tuple的完整資料。 物理上,虛擬ID並不一定被真的被儲存,例如,對於定長屬性, 其所有值按緊湊資料格式儲存,相應的虛擬ID就是陣列的下標。對於不定長屬性, 其不定長的資料內容儲存在連續儲存空間,用一個定長的指標來關聯真實值,而虛擬ID就是指標陣列的下標。

如下圖,左邊資料按BAT的格式組織,右邊描繪了該格式下執行SELECT Id,Name,Age,(Age-30) * 50 as Bonus FROM People WHERE Age > 30; 查詢的方式:

image.png

這樣儲存的好處有,第一,相同屬性的值儲存在一起,可以比較高效的應用一些壓縮演算法,減少其佔用儲存空間大小。 第二, 針對特定列的計算只需要讀取該特定列的值, 既可以較大提升儲存的I/O效能,也可以減少記憶體使用。第三, 每列資料按緊湊陣列的方式儲存,大部分針對屬性的計算簡化為對陣列值的遍歷計算,可以比較高效的利用CPU的計算能力(如SIMD等)。

計算模型

在以上列存格式的資料儲存的基礎上,MonetDB為應對資料密集型應用,專門研發了一套高效的查詢執行引擎,該引擎創造性的提出了一種新的執行模型,稱之為“Vectorized in-cache execution model”。

首先,和傳統的Pipelined模型一樣, MonetDB的查詢計劃是由各種運算元(operator)構成了一個樹(或DAG)。 但不同的是,傳統Pipelined模型中的運算元每次計算的物件是一個Tuple, 而MonetDB中,運算元每次計算的,是一組由相同屬性的不同屬性值構成的向量(陣列)。 在每個運算元內部,針對其計算屬性的型別,使用高度優化的程式碼(primitive)來處理向量資料。

向量是MonetDB執行模型中最基本的資料處理單元和傳輸單元(作為計算中間結果在運算元之間傳遞), 向量其實是一個由同一屬性的多個不同屬性值構成的一維陣列,根據屬性型別的異同,向量的大小也有差異。採用這種針對向量的計算模式的優勢是,每個運算元在計算過程中不在需要像傳統Pipelined模型裡那般,要從Tuple裡定位,讀取某些屬性,計算完成後再定位寫入到輸出Tuple中, 而是完全順序的記憶體讀寫,省去了這些Tuple解釋操作,即高效的利用了記憶體頻寬,也節省了data-access的時間。同時,為CPU自動優化SIMD指令提供了機會。

以下通過TPC-H Query 1 舉例說明MonetDB查詢執行過程:
image.png

左圖為查詢執行時資料在不同儲存單元的傳輸過程。MonetDB提出了一種名為ColumnBM的列式資料組織方式,可以在有限頻寬條件下,儘可能提升scan的效能。 磁碟和記憶體的資料都是壓縮資料,當他們被以向量的形式載入的CPU Cache時才被解壓,有助於提升記憶體利用率。不同列上的計算是分開的,直到最後才被物化為使用者需要的行式結果。

右圖是查詢執行計劃和向量資料流,整個查詢計劃從下到上有四類運算元: Scan, Select, Project, Aggregate。 但計算不同型別列的同種運算元,其內部程式碼也不同。 例如,負責處理`shipdate`列的Scan運算元,內部程式碼(primitive)是對Date資料型別的緊湊遍歷, 同時按條件篩選值。而負責處理`extprice`的Scan運算元,其內部程式碼(primitive)只是對Double的順序讀。所有運算元的輸入和輸出都是向量,整個查詢計劃是由向量構成的pipeline (詳細的向量計算演算法這裡不展開)。

由於主存頻寬無法滿足CPU-Efficient的程式碼(primitive)對資料的飢渴需求, MonetDB打算充分利用cache-memory頻寬來緩解問題。由此提出了in-cache計算: 所有的向量都被用特殊的方式組織,已使他們的大小可以完全放入CPU Cache。 因此,一些計算的結果可以被儲存在CPU Cache,而不需要寫回到記憶體。後續的計算可以直接讀取這些Cache中的結果,節省了主存讀的時間。

仍然以一個SQL查詢的例子說明計算過程:

SELECT l_quantity * l_extendedprice AS netto_value, 
              netto_value * l_tax AS tax_value,
              netto_value + tax_value AS total_value
FROM lineitem

image.png

如圖, ‘l_quantity * l_extendedprice’ 的結果‘netto_value’被儲存在CPU-Cache中,當計算‘tax_value’時,只需要從主存讀取‘l_tax’的向量值就可以了,同時,計算結果也被儲存在CPU-Cache中,當計算‘total_value’時不再需要從主存讀資料。由此,減輕了主存讀寫頻寬瓶頸對計算的限制。

右圖表明, “add”操作的效能要好於”mu1″和”mu2”, 因為它的輸入資料都在CPU Cache中。 同時,也證明了不同向量大小對計算效能的影響,當向量較小時(向量總數較多),沒有充分利用CPU-Cache的空間。向量較大時,因為無法全部放入CPU-Cache,導致部分資料必須從主存獲取。當向量大小接近CPU-Cache時,達到了平衡,此時的計算效能是最優。

效能分析

對於現代計算機的主要硬體, MonetDB有以下優化:

CPU : MonetDB查詢執行過程中,負責處理資料的程式碼(primitive)通常都是非常簡單的邏輯,即使有函式呼叫,也大都被設計為在一個函式呼叫中處理多組資料。大大減少了函式呼叫次數。對於單個資料來說,發生函式呼叫的overhead被多組資料所分攤。 加上SIMD等優化,每條資料計算所需的指令數被減少,也就得到了一個很低的instructions-per-tuple值。

Cache : 查詢過程中,各運算元之間以向量(vector)的形式交換資料,向量被設計為一個比較優化的大小,可以完全放入記憶體(fully cache-resident), 節省了主存資料讀寫的大延時。處理資料的程式碼(primitive)也被優化為採用cache-efficient的演算法來實現。

RAM : 資料按列儲存,並高效壓縮。相同容量的RAM可以放入更多的資料。

Disk : MonetDB中幾乎沒有隨機讀,所有的查詢計算都是基於Scan。 當資料需要儲存在磁碟上時,MonetDB優化了資料結構,形成了一套名為ColumnBM I/O Subsystem的機制來提升磁碟Scan的效能。

對照上文經典關係行存資料庫的理論分析的維度,分析MonetDB:

instruction-cache : 雖然MonetDB也採用pipelined的方式執行查詢,其各運算元的所需的指令總和也可能超過I-Cache的容量大小。但不同於Tuple-at-a-time模型,MonetDB中各運算元計算的是向量,CPU也需在不同運算元之間切換,但即使發生指令的cache-miss, 其重新load的時間可以被向量中所有資料所分攤。

Function call overhead : 絕大多數運算元實現都是對向量呼叫函式,而不是對單條資料的頻繁呼叫。大大減少了函式呼叫的次數。

Tuple manipulation : 所有運算元的輸入輸出都是向量的形式,即一個連續的緊湊陣列。 計算過程直接讀寫資料,不需要任何對資料做任何解釋。

Superscalar CPUs utilization : 因為計算都是針對向量的,即相同的指令處理一系列資料。MonetDB還特別針對資料處理程式碼(primitive)做了優化,使其儘可能不包含跳轉指令,充分發揮SuperScalar CPU向量計算的能力。

Data volume : 由於採用了按列儲存的方式,只有計算所需要的列才會被讀取,並且,列式資料被高效的壓縮,即節省了RAM空間,使其可以Keep更多的資料,也提高了Disk->RAM->Cache的頻寬利用率。

Benchmark

MonetDB中的資料讀是基於Scan的,因此資料儲存介質的不同,效能差異也會比較大。在實現中, MonetDB採用Memory mapped file的方式,將所有資料儲存對映為虛擬記憶體地址,從程式碼層面忽略了儲存介質的型別, 依賴系統的MMU換入實體記憶體地址以外的資料頁。在實際查詢計算過程中, MonetDB為了保證向量計算的效能,通常儘可能一次換入當前計算所需列的所有資料,如果真實資料的某一列的大小超過MonetDB可支配實體記憶體的總大小,針對該列的查詢效能損耗會比較嚴重,但相對來說,還是優於使用相同硬體儲存資料,並且未建索引的行存DBMS。

說到索引,MonetDB目前支援兩類索引 IMPRINTS和ORDERED 。但這兩類索引只支援定長數值型別,並且不支援複合索引。如果出現上面提到的情形,某列大小超過可支配實體記憶體,並且該列支援建索引,那麼建索引後可以極大提升查詢效能。

MonetDB paper中給出了TPC-H Query 1的查詢效能(如下), 證明其基於向量計算的效能已經接近hand-code C程式碼的效能:

image.png

為了體驗MonetDB, 以下是SQLite-In-Memory和MonetDB 的對比測試。

資料來自AMPLab : https://amplab.cs.berkeley.edu/benchmark 中的使用者訪問資料(uservisit),取樣了32,00萬資料,DDL語句:

CREATE TABLE "user_visit" (
    "sourceid"     BIGINT,
    "destid"       BIGINT,
    "sourceip"     VARCHAR(116),
    "desturl"      VARCHAR(100),
    "visitdate"    DATE,
    "adrevenue"    DOUBLE,
    "useragent"    VARCHAR(256),
    "countrycode"  CHAR(3),
    "languagecode" CHAR(6),
    "searchword"   VARCHAR(32),
    "duration"     INTEGER
);

其中,souceid,和 destid分別是對sourceip和desturl做Murmur雜湊得到的long值。

批量資料匯入: SQLite-In-Memory匯入到記憶體, MonetDB匯入到磁碟 ,原始CSV檔案大小6.3G :

DBMS DDL Cost /s Total Size/G
SQLite-In-Memory .mode csv;
.import raw.csv user_visit
347 6.37
MonetDB COPY INTO user_visit FROM raw.csv 88 2.2

MonetDB批量資料匯入速度是SQLite-In-Memory的4倍, 匯入後資料大小隻有原始檔案的1/3 。

查詢語句:

image.png

批量匯入後的第一次查詢, SQLite-In-Memory資料常駐記憶體,對其而言每次查詢效能穩定的, MonetDB資料持久化在磁碟,第一次查詢需要依賴mmu換入磁碟資料:

image.png

由於都沒有建索引,查詢都是基於Scan, 但SQLite-In-Memory Scan記憶體的效能要遠落後於MonetDB Scan磁碟。

第二次查詢,更改了查詢引數:

image.png

由於系統記憶體充足, 此時MonetDB的查詢幾乎是In-memory Scan, 查詢效能也提高了不少。

下面對sourceid和destid建索引:

DBMS DDL Cost /s Total Size/G
SQLite-In-Memory CREATE INDEX sourceIdIndex on user_visit (sourceId);
CREATE INDEX destIdIndex on user_visit(destId);
127 7.52
MonetDB CREATE ORDERED INDEX sourceIdIndex ON user_visit (sourceId);
CREATE ORDERED INDEX destIdIndex ON user_visit (destId);
12 2.7

可以利用索引的查詢有1,2,3,4,5。 SQLite-In-Memory在有索引的時查詢小於1ms, MonetDB的第一和第二次查詢延時如下圖:

image.png

在有索引的前提下,即使資料在磁碟,MonetDB的第一次查詢也可以在幾個毫秒內完成。

從以上Benchmark得到的結論:

  1. 對於MonetDB而言, 記憶體很重要。查詢的效能完全取決於資料是否In-Memory。 在實驗中,我們嘗試更換不同效能的磁碟,有SATA盤,SSD, 和記憶體掛載盤,其隨機讀效能有千百倍的差異,但對MonetDB的查詢影響不大(因為MonetDB依賴順序讀)。MonetDB Paper中宣稱在In-Memory模式下,單核可以每秒處理多達GB資料。
  2. 對定長列的查詢要比對不定長列查詢要快很多。
  3. 對有很多重複值的列的查詢要比對值是Unique的列的查詢快很多。
  4. 查詢列的個數對查詢效能影響不大。

參考資料

Zukowski M. Balancing vectorized query execution with bandwidth-optimized storage[J]. University of Amsterdam, PhD Thesis, 2009.

Abadi, Daniel, et al. “The design and implementation of modern column-oriented database systems.” Foundations and Trends® in Databases 5.3 (2013): 197-280.

Zukowski, Marcin, et al. “MonetDB/X100-A DBMS In The CPU Cache.” IEEE Data Eng. Bull. 28.2 (2005): 17-22.

Boncz, Peter A., Marcin Zukowski, and Niels Nes. “MonetDB/X100: Hyper-Pipelining Query Execution.” Cidr. Vol. 5. 2005.

https://www.monetdb.org


相關文章