乾貨丨時序資料庫DolphinDB與Spark的效能對比測試報告

語言: CN / TW / HK

1. 概述

Spark是基於記憶體計算的通用大資料平行計算框架,內建多種元件,如批處理、流處理、機器學習和圖處理。Hive是基於Hadoop的資料倉庫,支援類SQL的命令查詢,提升了Hadoop的易用性。Spark與Hive、Hadoop通常是搭配使用,利用Hive中的資料分割槽可以方便地管理和過濾資料,提高查詢效率。

DolphinDB是C++編寫的高效能分散式時序資料庫,內建高吞吐低延遲的列式記憶體引擎,集成了功能強大的程式語言,支援類Python和SQL的指令碼語言,可以直接在資料庫中進行復雜的程式設計和運算。DolphinDB內部用Data Source來抽象分割槽資料。在Data Source之上,可以完成SQL,機器學習,批處理和流處理等計算任務。一個Data Source既可以是內建的資料庫分割槽,也可以是外部資料。如果Data Source是內建資料庫的分割槽,大部分計算都可以在本地完成,極大地提升了計算和查詢效率。

本報告將對DolphinDB、Spark直接訪問HDFS(Spark+Hadoop,下文稱為Spark)、Spark通過Hive元件訪問HDFS(Spark+Hive+Hadoop,下文稱為Spark+Hive)三者進行效能對比測試。測試內容包括資料匯入、磁碟空間佔用、資料查詢以及多使用者併發查詢。通過對比測試,我們能更深入的瞭解影響效能的主要因素,以及不同工具的最佳應用場景。

2. 環境配置

2.1 硬體配置

本次測試使用了兩臺配置完全相同的伺服器(機器1,機器2),各個配置引數如下:

主機:DELL PowerEdge R730xd

CPU:Intel Xeon(R) CPU E5-2650 v4(24核 48執行緒 2.20GHz)

記憶體:512 GB (32GB × 16, 2666 MHz)

硬碟:17T HDD (1.7T × 10, 222 MB/s 讀取;210 MB/s 寫入)

網路:萬兆乙太網

OS: CentOS Linux release 7.6.1810 (Core)

2.2 叢集配置

測試的DolphinDB版本為Linux v0.95。測試叢集的控制節點部署在機器1上,每臺機器上各部署三個資料節點,共六個資料節點。每個資料節點配置8個worker,7個executor,24G記憶體。

測試的Spark版本為2.3.3,搭載Apache Hadoop 2.9.0。Hadoop與Spark配置為完全分散式模式,機器1為Master,並且在機器1、機器2上都具有Slave。Hive的版本是1.2.2,機器1、機器2上都具有Hive。元資料儲存在機器1上的MySql資料庫中。Spark 與Spark + Hive使用Standalone模式下的client 方式來提交應用。

測試時,DolphinDB、Spark、Spark+Hive均配置6塊硬碟,不同併發數下使用的CPU、記憶體總和都相同,都是48個執行緒,144G記憶體。Spark與Spark+Hive使用的資源只是對於特定的應用,每個應用有6個executor,在多使用者併發的情況下,Spark、Spark+Hive單個使用者使用的資源會隨著使用者數量增多而減少。不同併發數下每個使用者使用的資源如表1所示。

表1.Spark、Spark+Hive不同併發數下單使用者使用的資源

3. 資料集及資料庫設計

3.1 資料集

測試資料集是紐約證券交易所(NYSE)提供的TAQ資料集,包含 8000 多支股票在2007.08.01-2007.08.31一個月內的Level 1報價資料,包含交易時間, 股票程式碼, 買入價, 賣出價, 買入量, 賣出量等報價資訊。資料集中共有 65 億(6,561,693,704)條報價記錄,一個 CSV 中儲存一個交易日的記錄,該月共23個交易日,未壓縮的 23個CSV 檔案共計 277 GB。

資料來源:https://www.nyse.com/market-data/historical

3.2 資料庫設計

表2. TAQ在各個系統中的資料型別。

在 DolphinDB database 中,我們按照date、symbol列組合分割槽,第一分割槽使用日期DATE來進行值分割槽,共23個分割槽,第二分割槽使用股票程式碼SYMBOL來進行範圍分割槽,分割槽數量100個,每個分割槽大約120M左右。

Spark儲存在HDFS上的資料以23個csv對應23個目錄。Spark+Hive採用兩層分割槽,第一層分割槽使用日期DATE列進行靜態分割槽,第二層分割槽使用股票程式碼SYMBOL進行動態分割槽。

具體指令碼見附錄。

4. 資料匯入和查詢測試

4.1 資料匯入測試

原始資料均勻地分佈在兩臺伺服器的6個硬碟上,這樣可以充分利用叢集中的所有資源。DolphinDB通過非同步多節點的方式並行匯入資料,Spark與Spark+Hive並行啟動6個應用來讀取資料,把資料儲存到HDFS中。各個系統匯入資料的時間如表3所示。各個系統中資料佔用的磁碟空間如表4所示。資料匯入指令碼見附錄。

表3. DolphinDB、Spark、Spark+Hive匯入資料時間

表4. DolphinDB、Spark、Spark+Hive中資料佔用的磁碟空間

DolphinDB的匯入效能明顯優於Spark和Spark+Hive,是Spark的4倍左右,是Spark + Hive的6倍左右。DolphinDB使用C++編寫並且內部有很多優化,極大地利用了磁碟的IO。

DolphinDB佔用的磁碟空間大於Spark與Spark+Hive,大約是他們的2倍,這是因為Spark和Spark+Hive在Hadoop上都使用Parquet格式,Parquet格式通過Spark寫入到Hadoop上預設使用snappy壓縮。

4.2 資料查詢測試

為保證測試公平,每個查詢語句要進行多次測試,每次測試之前均通過 Linux 系統命令分別清除系統的頁面快取、目錄項快取和硬碟快取。DolphinDB還清除其內建快取。

表5中的查詢語句涵蓋了大部分查詢場景,包含分組、排序、條件、聚合計算、點查詢、全表查詢,用來評估DolphinDB、Spark、Spark+Hive在不同使用者數量提交下的效能。

表5. DolphinDB、Spark、Spark+Hive查詢語句

4.2.1 DolphinDB與Spark單使用者查詢測試

以下是DolphinDB與Spark單使用者查詢的結果,結果中的耗時為查詢8次的平均用時。

表6. DolphinDB、Spark單使用者查詢結果

從結果可以看出,DolphinDB的查詢效能是Spark+HDFS的200倍左右。查詢Q1到Q6都是以DolphinDB的分割槽欄位為過濾條件,DolphinDB只需要載入指定分割槽的資料,無需全表掃描,而Spark從Q1到Q6都需要全表掃描,耗費大量的時間。對於查詢Q7,DolphinDB和Spark都需要全表掃描,但是DolphinDB只加載相關的列,無需載入所有列,而Spark則需要載入所有資料。由於Query執行時間被資料載入主導,DolphinDB和Spark的效能差距沒有之前的查詢語句的大。

4.2.2 DolphinDB與Spark+Hive單使用者查詢測試

由於DolphinDB的資料經過分割槽,且在查詢的時候實現謂詞下推,效率明顯高於Spark。此處我們使用Spark搭載Hive元件來訪問HDFS,對比DolphinDB和Spark+Hive的查詢效能。以下是DolphinDB、Spark+Hive單使用者查詢的結果,結果中的耗時為查詢8次的平均用時。

表7. DolphinDB、Spark+Hive單使用者查詢結果

結果顯示,DolphinDB的查詢效能明顯優於Spark+Hive,是Spark+Hive的數十倍。與表6的結果相比,Spark+Hive的查詢速度比Spark要快得多,DolphinDB具有的優勢明顯下降了很多。這是因為Hive對資料進行分割槽,且在查詢語句的條件帶有分割槽欄位的時候,只加載部分資料,實現資料過濾,提高效率。查詢語句Q7掃描全表的時候會出現記憶體溢位。

DolphinDB、Spark+Hive都對資料進行了分割槽,且在載入資料時都可以實現謂詞下推,達到資料過濾的效果,但是DolphinDB的查詢速度優於Spark+Hive。這是因為Spark+Hive區讀取HDFS上的資料是不同系統之間的訪問,資料要經過序列化、網路傳輸、反序列化的過程,非常耗時,從而影響效能。DolphinDB的大部分計算都在本地完成,減少了資料傳輸,因此更加高效。

4.2.3 DolphinDB與Spark計算能力對比

上面DolphinDB分別與Spark、Spark+Hive的查詢效能對比,由於資料分割槽、查詢時的資料過濾以及傳輸影響了Spark的效能,因此這裡我們先把資料載入到記憶體中,再進行相關的計算,比較DolphinDB和Spark+Hive。我們省略了Spark+Hive,因為使用Hive只是為了資料過濾,讀取HDFS上的資料更加高效,這裡的測試資料已經在記憶體中。

表8是測試計算能力的語句。每次測試都包含兩個語句,第一個語句是把資料載入到記憶體中,第二個語句是對記憶體中的資料進行計算。DolphinDB會自動快取資料,Spark則通過自己的預設快取機制重新建立一個臨時表TmpTbl。

表8. DolphinDB與Spark計算能力對比語句

以下是DolphinDB與Spark計算能力的測試結果,結果中的耗時是測試5次的平均用時。

表9. DolphinDB與Spark計算能力測試結果

由於資料已經在記憶體中,對比表6,Spark使用的時間大幅度減少,但是DolphinDB的計算能力仍然比Spark優越。DolphinDB用C++編寫,自己管理記憶體,比起Spark使用JVM來管理記憶體更加高效。另外,DolphinDB內建了更高效的演算法,提高了計算效能。

DolphinDB的分散式計算以分割槽為單位,計算指定記憶體的資料。Spark載入整個HDFS上的塊,一個數據塊包含了具有不同symbol值的資料,雖然快取,但是仍然要篩選,所以在Q1與Q2的比值較大。Spark計算時使用的廣播變數是經過壓縮的,傳輸到其他的executor上再解壓影響效能。

4.2.4 多使用者併發查詢

我們使用表5中的查詢語句,對DolphinDB、Spark、Spark+Hive進行多使用者併發查詢測試。以下是測試結果,結果中的耗時是查詢8次的平均用時。

表10. DolphinDB、Spark、Spark+Hive多使用者併發查詢結果

圖1. DolphinDB、Spark多使用者查詢結果對比

圖2. DolphinDB、Spark+Hive多使用者查詢結果對比

從上面的結果可以看出,隨著併發數量的增加,三者的查詢時間逐漸增加。當達到8個使用者併發的時候Spark效能較之前少量的使用者併發情況下顯著下降,Spark 在執行Q7的時候會導致worker死亡。Spark+ Hive在多使用者訪問的時候與DolphinDB一樣,基本保持穩定,但是執行Q7查詢語句的一直會出現記憶體溢位的異常。

Spark+ Hive的查詢配置與Spark 一樣,因為有分割槽的作用,並且可以過濾資料,查詢資料量比較小,所以效率相對於Spark掃描全部資料比較好。

DolphinDB在併發查詢中效能明顯優於Spark 與Spark+ Hive,從上圖可以看出在多使用者併發訪問情況下,隨著使用者數量的增加,DolphinDB相對於Spark 的優勢幾乎是線性增長,相對於Spark + Hive 的優勢基本保持不變,體現了有資料分割槽在查詢的時候實現資料過濾的重要性。

DolphinDB在多使用者併發的情況下實現了多使用者的資料共享,不像Spark 的資料只是針對於具體的應用。所以在8個併發使用者的情況下,Spark 每個使用者分配到的資源比較少,效能顯著下降。DolphinDB的資料共享可以減少資源的使用,在有限的資源下,把更多的資源留給使用者計算使用,提高使用者併發的效率,增大使用者的併發數量。

5. 小結

在資料的匯入方面,DolphinDB可以並行載入,Spark與Spark+Hive 則通過多個應用同時載入來匯入資料。DolphinDB的匯入速度是Spark 和Spark+ Hive 的4-6倍。在磁碟空間上,DolphinDB佔用的磁碟空間是Spark與Spark+ Hive在Hadoop上佔用的磁碟空間的兩倍左右,Spark與Spark + Hive使用了snappy壓縮。

在資料的SQL查詢方面,DolphinDB的優勢更加明顯。優勢主要來自四個方面:(1)本地化計算,(2)分割槽過濾,(3)優化的記憶體計算,(4)跨會話的資料共享。在單使用者查詢情況下,DolphinDB的查詢速度是Spark的幾倍到上百倍,是Spark+ Hive 的幾十倍。Spark 讀取HDFS 是不同的系統之間的呼叫,其中包含了資料的序列化,網路,反序列化非常消耗時間,且佔據很多的資源。DolphinDB的SQL查詢大部分是本地化計算,大幅減少了資料傳輸和載入的時間。Spark+ Hive 相對與Spark速度提升很大,主要是因為Spark + Hive只掃描相關分割槽的資料,實現了資料的過濾。在剔除本地化和分割槽過濾的因素後(即所有資料已經在記憶體中),DolphinDB的計算能力仍然優於Spark數倍。DolphinDB基於分割槽的分散式計算效率很高,且對記憶體的管理比Spark基於JVM的管理更加優秀。Spark的多使用者併發會隨著使用者數量的增多效率逐漸下降,在查詢大資料量的時候使用者過多導致worker 死亡。Spark + Hive的多使用者併發相對比較穩定,但是載入資料過大會出現記憶體溢位錯誤。 多使用者情況下, DolphinDB可以實現資料的共享,從而減少載入資料使用的資源,查詢速度是Spark的數百倍,是Spark+Hive 的幾十倍。隨著使用者數量的增加,DolphinDB相對於Spark的效能優勢更加明顯。涉及到分割槽查詢的情況下,Spark+ Hive與DolphinDB顯著提高查詢效能。

Spark是一個非常優秀的通用分散式計算引擎,在SQL查詢、批處理、流處理、機器學習等方面均有上佳表現。但由於SQL查詢通常只需要對資料計算一次,相對於機器學習需要上百次的迭代,記憶體計算的優勢無法充分體現。因此,我們更建議將Spark用於計算密集型的機器學習。

在測試過程中,我們也發現DolphinDB是一個非常輕量級的實現,叢集的搭建簡單快速, Spark + Hive+ Hadoop 叢集安裝配置非常複雜。

附錄

附錄1. 資料預覽

附錄2. Hive建立表語句

CREATE TABLE IF NOT EXISTS TAQ (time TIMESTAMP, bid DOUBLE, ofr DOUBLE, bidsiz INT, ofrsiz INT, mode INT, ex TINYINT, mmid STRING)PARTITIONED BY (date DATE, symbol STRING) STORED AS PARQUET;

附錄3.

DolphinDB匯入資料指令碼:

fps1、fps2分別代表機器1、2上所有的csv路徑的vector
fps是包含fps1和fps2 的vector
allSites1、allSites2 分別代表機器1、2上資料節點名稱的vector
allSite 是包含 allSites1和allSites2的vector
DATE_RANGE=2007.07.01..2007.09.01
date_schema=database('', VALUE, DATE_RANGE)
symbol_schema=database('', RANGE, buckets)
db=database(FP_DB, COMPO,[date_schema,symbol_schema])
taq = db.createPartitionedTable(schema, `taq, `date`symbol)
for(i in 0..1){
	for(j in 0..(size(fps[i])-1))  {
		rpc(allSites[i][j] % size(allSite[i])],submitJob,"loadData" , "loadData" ,loadTextEx{database(FP_DB), "taq", `date`symbol, fps[i][j]} )
	}
}

Spark與Hive匯入資料的配置:

--master local[8]
--executor-memory 24G