乾貨丨時序數據庫分區教程(二)

1.分區原則

分區的總原則是讓數據管理更加高效,提升查詢和計算的性能,達到低延時和高吞吐量。node

1.1 選擇合適的分區字段數據庫

DolphinDB分區字段的數據類型能夠是整型、日期類型和SYMBOL類型。注意,STRING、FLOAT和DOUBLE數據類型不能夠做爲分區字段。數組

雖然DolphinDB支持TIME、SECOND、DATETIME類型字段的分區,可是在實際使用中要謹慎使用,避免採用值分區,以避免分區粒度過細,將大量時間耗費在建立或查詢幾百上千萬個只包含幾條記錄的文件目錄。服務器

分區字段應當是在業務中至關重要的。例如在證券交易領域,許多任務都與股票交易日期或股票代碼相關,所以使用這兩個字段來分區比較合理。併發


1.2 分區粒度不要過大app

DolphinDB單個分區支持最大記錄條數是20億條。但合理的記錄條數應該遠遠小於這個數。一個分區內的多個列以文件形式獨立存儲在磁盤上,一般數據是通過壓縮的。使用的時候,系統從磁盤讀取所須要的列,解壓後加載到內存。若是分區粒度過大,可能會形成多個工做線程並行時內存不足,或者致使系統頻繁地在磁盤和工做內存之間切換,影響性能。一個經驗公式是,數據節點的可用內存是S,工做線程(worker)的的數量是W,則建議每一個分區解壓後在內存中的大小不超過S/8W。假設工做內存上限32GB,8工做線程,建議單個分區解壓後的大小不超過512MB。負載均衡

DolphinDB的子任務以分區爲單位。所以分區粒度過大會形成沒法有效利用多節點多分區的優點,將原本能夠並行計算的任務轉化成了順序計算任務。分佈式

DolphinDB是爲OLAP的場景優化設計的,支持添加數據,不支持對個別行進行刪除或更新。若是要修改數據,以分區爲單位覆蓋所有數據。若是分區過大,下降效率。DolphinDB在節點之間複製副本數據時,一樣以分區爲單位,分區過大,不利於數據在節點之間的複製。ide

綜上各類因素,建議一個分區未壓縮前的原始數據大小控制在100M~1G之間。固然這個數字可結合實際狀況調整。譬如在大數據應用中,咱們常常看到寬表設計,一個表達到幾百個字段,可是在單個應用中只會使用一部分字段。這種狀況下,能夠適當放大上限的範圍。函數

若是發現分區粒度過大,能夠採用幾種方法,(1)採用組合分區(COMPO),(2)增長分區個數,(3)將範圍分區改成值分區。


1.3 分區粒度不要太小

分區粒度太小,一個查詢和計算做業每每會生成大量的子任務,這會增長數據節點和控制節點,以及控制節點之間的通信和調度成本。分區粒度太小,也會形成不少低效的磁盤訪問(小文件讀寫),形成系統負荷太重。另外,全部的分區的元數據都會駐留在控制節點的內存中。分區粒度太小,分區數過多,可能會致使控制節點內存不足。咱們建議每一個分區未壓縮前的數據量不要小於100M。

譬如股票的高頻交易數據若按交易日期和股票代碼的值作組合分區,會致使許多極小的分區,由於許多交易不活躍的股票的交易數據量太少。若是將股票代碼的維度按照範圍分區的方法來切分數據,將多個交易不活躍的股票組合在一個分區內,則能夠有效解決分區粒度太小的問題,提升系統的性能。

2.如何把數據均勻分區

當各個分區的數據量差別很大時,會形成系統負荷不均衡,部分節點任務太重,而其餘節點處於閒置等待狀態。當一個任務有多個子任務時,只有最後一個子任務完成了,纔會把結果返回給用戶。因爲一個子任務對應一個分區,若是數據分佈不均勻,可能會增大做業延時,影響用戶體驗。

爲了方便根據數據的分佈進行分區,DolphinDB提供了一個很是有用的函數cutPoints(X, N, [freq])。X是一個數據,N表示產生的分組數,freq是與X等長的數組,每一個元素對應X中元素出現的頻率。這個函數返回具備(N+1)個元素的數組,使得X中的數據均勻地分佈在N個組中。

下面的例子中,須要對股票的報價數據按日期和股票代碼兩個維度作數據分區。若是簡單的按股票的首字母進行範圍分區,極易形成數據分佈不均,由於極少許的股票代碼以U, V, X,Y,Z等字母開頭。建議使用cutPoints函數根據樣本數據來劃分分區。

// 將2007.08.01這天數據導入
t = ploadText(WORK_DIR+"/TAQ20070801.csv")

// 選擇2007.08.01這天數據的股票代碼的分佈來計算分組規則
t=select count(*) as ct from t where date=2007.08.01 group by symbol

// 按照股票代碼字母順序產生128個區間。每一個區間內部的數據行數在2007.08.01這天是至關的。
buckets = cutPoints(t.symbol, 128, t.ct)

// 最後一個區間的結束邊界由2007.08.01的數據決定。爲排除2007.08.01以後以後有新的將最後一個區間的結束邊界替換成不會出現的最大的股票代碼。
buckets[size(buckets)-1] = `ZZZZZ

//buckets的結果以下:
//["A",'ABA','ACEC','ADP','AFN','AII','ALTU','AMK',..., 'XEL','XLG','XLPRACL','XOMA','ZZZZZ']

dateDomain = database("", VALUE, 2017.07.01..2018.06.30)
symDomain = database("", RANGE, buckets)
stockDB = database("dfs://stockDBTest", COMPO, [dateDomain, symDomain])

除了使用範圍分區的方法,列表分區也是解決數據分佈不均勻的有效方法。

3.時序類型分區

時間是實際數據中最多見的一個維度。DolphinDB提供了豐富時間類型以知足用戶的需求。當咱們以時間類型字段做爲分區字段時,在時間取值上須要預留足夠的空間以容納未來的數據。下面的例子,咱們建立一個數據庫,以天爲單位,將2000.01.01到2030.01.01的日期分區。注意,只有當實際數據寫入數據庫時,數據庫纔會真正建立須要的分區。

dateDB = database("dfs://testDate", VALUE, 2000.01.01 .. 2030.01.01)

DolphinDB使用時間類型做爲分區字段時,還有一個特殊的優勢。數據庫定義的分區字段類型和數據表實際採用的時間類型能夠不一致,只要保證定義的分區字段數據類型精度小於等於實際數據類型便可。好比說,若是數據庫是按月(month)分區,數據表的字段能夠是month, date, datetime, timestamp和 nanotimestamp。系統自動會做數據類型的轉換。

4.不一樣表相同分區的數據存放於同一節點

在分佈式數據庫中,若是多個分區的數據表要鏈接(join)一般十分耗時,由於涉及到的分區可能在不一樣的節點上,須要在不一樣節點之間複製數據。爲解決這個問題,DolphinDB推出了共存儲位置的分區機制。DolphinDB確保同一個分佈式數據庫裏全部表在相同分區的數據存儲在相同的節點上。這樣的安排,保證了這些表在鏈接的時候很是高效。DolphinDB當前版本對採用不一樣分區機制的多個分區表不提供鏈接功能。

dateDomain = database("", VALUE, 2018.05.01..2018.07.01)
symDomain = database("", RANGE, string('A'..'Z') join `ZZZZZ)
stockDB = database("dfs://stockDB", COMPO, [dateDomain, symDomain])

quoteSchema = table(10:0, `sym`date`time`bid`bidSize`ask`askSize, [SYMBOL,DATE,TIME,DOUBLE,INT,DOUBLE,INT])
stockDB.createPartitionedTable(quoteSchema, "quotes", `date`sym)

tradeSchema = table(10:0, `sym`date`time`price`vol, [SYMBOL,DATE,TIME,DOUBLE,INT])
stockDB.createPartitionedTable(tradeSchema, "trades", `date`sym)

上面的例子中,quotes和trades兩個分區表採用同一個分區機制。

DolphinDB是爲OLAP設計的系統,主要是解決海量結構化數據的快速存儲和計算,以及經過內存數據庫和流數據實現高性能的數據處理。DolphinDB不適合數據頻繁更改的OLTP業務系統。DolphinDB的數據寫入與Hadoop HDFS相似,快速在每一個分區或文件的末尾批量插入數據。插入的數據會壓縮存儲到磁盤,通常壓縮比例在20%~25%。數據一旦追加到基於磁盤的數據表後,不能快速更新或刪除某些符合條件的記錄,必須以分區爲單位對數據表進行修改。這也是分區原則中提到單個分區不宜過大的緣由之一。

5.多副本機制

DolphinDB容許爲每個分區保留多個副本,默認的副本個數是2,能夠修改控制節點的參數dfsReplicationFactor來設置副本數量。

設置冗餘數據的目的有兩個: (1)當某個數據節點失效或者或磁盤數據損壞時,系統提供容錯功能繼續提供服務; (2)當大量併發用戶訪問時,多副本提供負載均衡的功能,提升系統吞吐量,下降訪問延時。

DolphinDB經過兩階段事務提交機制,確保數據寫入時,同一副本在多節點之間的數據強一致性。

在控制節點的參數文件controller.cfg中,還有一個很是重要的參數dfsReplicaReliabilityLevel。 該參數決定是否容許多個副本駐留在同一臺物理服務器的多個數據節點上。在development階段,容許在一個機器上配置多個節點,同時容許多個副本駐留在同一臺物理服務器(dfsReplicaReliabilityLevel=0), 可是production階段須要設置成爲1,不然起不到容錯備份的做用。

 // 每一個表分區或文件塊的副本數量。默認值是2。
dfsReplicationFactor=2

 // 多個副本是否能夠駐留在同一臺物理服務器上。 Level 0:容許; Level 1:不運行。默認值是0。
dfsReplicaReliabilityLevel=0

6.事務機制

DolphinDB對基於磁盤(分佈式文件系統)的數據庫表的讀寫支持事務,也就是說確保事務的原子性,一致性,隔離性和持久化。DolphinDB採用多版本機制實現快照級別的隔離。在這種隔離機制下,數據的讀操做和寫操做互相不阻塞,能夠最大程度優化數據倉庫讀的性能。

爲了最大程序優化數據倉庫查詢、分析、計算的性能,DolphinDB對事務做了一些限制:

首先,一個事務只能包含寫或者讀,不能同時進行寫和讀。

其次,一個寫事務能夠跨越多個分區,可是同一個分區不能被多個writer併發寫入。也就是說當一個分區被某一個事務A鎖定了,另外一個事務B試圖再次去鎖定這個分區時,系統馬上會拋出異常致使事務B失敗回滾。

7.多Writer並行寫入

DolphinDB提供了強大的分區機制,單個數據表能夠支持幾百萬的分區數量,這爲高性能的並行數據加載創造了條件。特別是當將海量的數據從別的系統導入到DolphinDB時,或者須要將實時數據以準實時的方式寫入到數據倉庫時,並行加載顯得尤其重要。

下面的例子將股票報價數據(quotes)並行加載到數據庫stockDB。stockDB以日期和股票代碼作複合分區。數據存儲在csv文件中,每一個文件保存一天的quotes數據。

//建立數據庫和數據表
dateDomain = database("", VALUE, 2018.05.01..2018.07.01)
symDomain = database("", RANGE, string('A'..'Z') join `ZZZZZ)
stockDB = database("dfs://stockDB", COMPO, [dateDomain, symDomain])
quoteSchema = table(10:0, `sym`date`time`bid`bidSize`ask`askSize, [SYMBOL,DATE,TIME,DOUBLE,INT,DOUBLE,INT])
stockDB.createPartitionedTable(quoteSchema, "quotes", `date`sym)

def loadJob(){
	fileDir='/stockData'

    // 到路徑下取出數據文件名
	filenames = exec filename from files(fileDir)

	// 加載數據庫
	db = database("dfs://stockDB")

	// 對每一個文件,經過文件名產生jobId前綴。
	// 經過函數submitJob提交後臺程序調用loadTextEx將數據加載到stockDB數據庫中。
	for(fname in filenames){
		jobId = fname.strReplace(".txt", "")
		submitJob(jobId,, loadTextEx{db, "quotes", `date`sym, fileDir+'/'+fname})
	}
}

//經過pnodeRun將loadJob這個任務發送到集羣的每一個數據節點進行並行加載。
pnodeRun(loadJob)

當多個writer並行加載數據時,要確保這些writer不會同時往同一個分區寫入數據,不然會致使事務失敗。在上面的例子中,每個文件存儲了一天的數據,而quotes表的一個分區字段是日期,從而確保全部加載數據的做業不會產生有重疊的事務。


歡迎訪問 官網下載DolphinDB試用版

相關文章
相關標籤/搜索