超大數(shù)據(jù)下大批量隨機(jī)鍵值的查詢優(yōu)化方案

一、問(wèn)題描述

鍵值查詢是很常見(jiàn)的查詢場(chǎng)景,在數(shù)據(jù)表上建有索引后,即使表中數(shù)據(jù)記錄數(shù)巨大(幾億甚至幾十億行),用鍵值查詢出單條記錄也會(huì)很快,因?yàn)榻⑺饕蟮膹?fù)雜度只有 logN(以 2 為底)次, 10 億行數(shù)據(jù)也只要比較 30 次(10 億約等于 2^30),在現(xiàn)代計(jì)算機(jī)上也只需要數(shù)十毫秒而已。

創(chuàng)新互聯(lián)是一家業(yè)務(wù)范圍包括IDC托管業(yè)務(wù),網(wǎng)站空間、主機(jī)租用、主機(jī)托管,四川、重慶、廣東電信服務(wù)器租用,達(dá)州托管服務(wù)器,成都網(wǎng)通服務(wù)器托管,成都服務(wù)器租用,業(yè)務(wù)范圍遍及中國(guó)大陸、港澳臺(tái)以及歐美等多個(gè)國(guó)家及地區(qū)的互聯(lián)網(wǎng)數(shù)據(jù)服務(wù)公司。

不過(guò),如果需要查詢的鍵值很多,比如多達(dá)幾千甚至幾萬(wàn)的時(shí)候,如果每次都獨(dú)立查找,那讀取和比較也會(huì)累積到幾萬(wàn)甚至幾十萬(wàn)次,時(shí)間延遲由此也會(huì)漲到幾十分鐘甚至小時(shí)級(jí)別,這時(shí)候簡(jiǎn)單地使用數(shù)據(jù)庫(kù)索引對(duì)于用戶體驗(yàn)必然是難以容忍的了。

二、場(chǎng)景舉例

下面我們要介紹的集算器組表功能,基于高性能索引和批量鍵值查找,可以有效地應(yīng)對(duì)這種場(chǎng)景。我們會(huì)按照以下幾種順序逐步深入討論:

1)單字段鍵

2)多字段鍵

3)多線程查詢

4)數(shù)據(jù)追加的處理

需要說(shuō)明的,本文只研討單機(jī)的情況,后續(xù)還有文章會(huì)繼續(xù)深入討論基于集群的方案。

2.1 單字段鍵

我們以下表這種比較典型的數(shù)據(jù)結(jié)構(gòu)為例:

字段名稱

類型

是否主鍵

說(shuō)明

id

long

從 1 開(kāi)始自增

data

string


需要獲取的數(shù)據(jù)

2.1.1創(chuàng)建組表

首先我們創(chuàng)建一個(gè)組表,把源數(shù)據(jù)導(dǎo)入組表:


A

1

=file("single.ctx")

2

=A1.create(#id,data)

3

=file("single_source.txt")

4

=A3.cursor@t()

5

=A2.append(A4)

A1:建立并打開(kāi)指定文件對(duì)象,這里的 single.ctx 是將要?jiǎng)?chuàng)建的組表文件,擴(kuò)展名用 ctx。關(guān)于組表的詳細(xì)定義和使用方法可以參考集算器教程。

A2:創(chuàng)建組表的數(shù)據(jù)結(jié)構(gòu)為(id,data)。其中,# 開(kāi)頭的字段稱為維字段,組表將假定數(shù)據(jù)按該字段有序,也就是組表 A2 將對(duì)鍵字段 id 有序。組表默認(rèn)使用列存方式。

A3:假定源數(shù)據(jù)以文本方式存儲(chǔ),A3 打開(kāi)數(shù)據(jù)文件。這個(gè) txt 文件的數(shù)據(jù)表頭以及前幾行部分?jǐn)?shù)據(jù)如下圖所示。當(dāng)然,源數(shù)據(jù)也可以來(lái)自數(shù)據(jù)庫(kù)等其它類型的數(shù)據(jù)源。

超大數(shù)據(jù)下大批量隨機(jī)鍵值的查詢優(yōu)化方案

A4:針對(duì)源數(shù)據(jù)生成游標(biāo)。其中 @t 選項(xiàng)指明文件中第一行記錄是字段名。

A5:將游標(biāo) A4 中的記錄追加寫入組表。

上面的腳本假定主鍵 id 在原始數(shù)據(jù)中已經(jīng)是有序的了,如果實(shí)際情況的主鍵并非有序,那就需要先將主鍵排序后再建為組表。這時(shí)可以使用cs.sortx()函數(shù)排序,具體方法詳見(jiàn)函數(shù)參考。

在集算器的設(shè)計(jì)器中,通過(guò)三行代碼,可以直觀看到其中前十條數(shù)據(jù),代碼和截圖如下所示:


A

1

=file("single.ctx")

2

=A1.create()

3

=A2.cursor().fetch(10)

A1:打開(kāi)指定文件名的組表文件對(duì)象。

A2:f.create(),函數(shù)中無(wú)參數(shù)時(shí)則直接打開(kāi)組表。

A3:使用游標(biāo)訪問(wèn) A2 中的前十條數(shù)據(jù),如下圖。

超大數(shù)據(jù)下大批量隨機(jī)鍵值的查詢優(yōu)化方案

2.1.2 創(chuàng)建索引

接下來(lái),我們?yōu)榻M表文件建立索引,以提升檢索性能:


A

1

=file("single.ctx")

2

=A1.create()

3

=A2.index(id_idx;id;data)

A1:打開(kāi)指定文件名的組表文件對(duì)象。

A2:使用無(wú)參數(shù)的 create 函數(shù)打開(kāi)組表。

A3:建立索引。在函數(shù) T.index() 中,參數(shù) id_idx 是索引名稱,id 是維,data 是數(shù)據(jù)列。一般情況下,建索引并不需要使用數(shù)據(jù)列,在用索引查找時(shí)會(huì)到原數(shù)據(jù)表中再去找數(shù)據(jù)。不過(guò),本例在建立索引時(shí)將數(shù)據(jù)列也包含進(jìn)了索引,這樣查找時(shí)就不再引用數(shù)據(jù)列了,雖然占用的空間會(huì)大一些,但是查找的也會(huì)更快一些。

按維字段建索引時(shí)會(huì)利用組表已經(jīng)有序的特點(diǎn)不再排序。如果開(kāi)始建組表時(shí)沒(méi)有使用 #,那么這時(shí)建索引時(shí)就會(huì)重新排序。

2.1.3查詢

使用主、子程序調(diào)用的方式來(lái)完成查詢:

查詢子程序腳本 search.dfx:


A

1

=file("single.ctx")

2

=A1.create()

3

=keys

4

=A2.icursor(;A3.contain(id),id_idx)

5

>file("result.txt").export@t(A4)

A3:keys 是參數(shù),由下面的主程序在調(diào)用時(shí)傳遞。

A4:在組表的 icursor()這個(gè)函數(shù)中,使用索引 id_idx,以條件 A3.contain(id) 來(lái)過(guò)濾組表。集算器會(huì)自動(dòng)識(shí)別出 A3.contain(id) 這個(gè)條件可以使用索引,并會(huì)自動(dòng)將 A3 的內(nèi)容排序后從前向后查找。

A5:將 A4 中查詢出的結(jié)果導(dǎo)出至 result.txt。這里 @t 選項(xiàng)指定導(dǎo)出時(shí)將輸出字段名。

主程序腳本:


A

1

=file("keys.txt").import@i()

2

=call("search.dfx",A1)

A1:從keys.txt獲取查詢鍵值序列,因?yàn)橹挥幸涣薪Y(jié)果,可以使用 @i 選項(xiàng),將結(jié)果返回成序列:

超大數(shù)據(jù)下大批量隨機(jī)鍵值的查詢優(yōu)化方案

這個(gè)序列就是需要進(jìn)行查詢的隨機(jī)鍵值集。例子中使用 keys.txt 來(lái)預(yù)先存好隨機(jī)的鍵值,實(shí)際應(yīng)用中,也可以用其他數(shù)據(jù)源來(lái)存儲(chǔ)。

A2:調(diào)用子程序 serach.dfx,把 A1 獲得的鍵值集作為參數(shù)傳遞給子程序。

下面就是結(jié)果文件 result.txt 中的部分內(nèi)容:

超大數(shù)據(jù)下大批量隨機(jī)鍵值的查詢優(yōu)化方案

另外,我們還可以將集算器嵌入到 Java 應(yīng)用程序中,從而為 Java 應(yīng)用提供靈活、簡(jiǎn)便的數(shù)據(jù)查詢能力。嵌入時(shí)可以像用 JDBC 訪問(wèn)數(shù)據(jù)庫(kù)那樣訪問(wèn)集算器腳本。具體的寫法可以參閱教程《被 JAVA 調(diào)用》一節(jié)。

本例的單字段鍵查詢示例,在數(shù)據(jù)結(jié)構(gòu)上較為簡(jiǎn)單。其中查詢的鍵值字段為 id,需要獲取的數(shù)據(jù)為單列的 data,如果還有其它列,例如:

字段名稱

類型

是否主鍵

說(shuō)明

id

Long

從 1 開(kāi)始自增

data1

String


需要獲取的數(shù)據(jù) 1

data2

Int


需要獲取的數(shù)據(jù) 2

……

……


……

dataN

……


需要獲取的數(shù)據(jù) N

那么在建立索引步驟時(shí),就應(yīng)該包含多個(gè)數(shù)據(jù)列字段,數(shù)據(jù)列參數(shù)的寫法如下所示:


A

1

=file("single.ctx")

2

=A1.create()

3

=A2.index(id_idx;id;data1,data2,…,dataN)

在接下來(lái)要討論的多字段鍵情況中,建索引時(shí)需要建立多個(gè)索引字段,對(duì)應(yīng)參數(shù)部分也有類似的寫法:index(id_idx;id1,id2,…,idN;data1,data2,…,dataN)。

2.2多字段鍵

多字段健指的是聯(lián)合主鍵的情況,例如:

字段名稱

類型

是否主鍵

說(shuō)明

type

string


可枚舉

Id

long


每種枚舉類型的 id 都從 1 開(kāi)始自增

data

string


需要獲取的數(shù)據(jù)

其中 type 和 id 兩個(gè)字段作為聯(lián)合主鍵確定一條記錄。

2.2.1 方法一(通用方法)

2.2.1.1 創(chuàng)建組表

A

1

=file("multi.ctx")

2

=A1.create(#type,#id,data)

3

=file("multi_source.txt")

4

=A3.cursor@t()

5

=A2.append(A4)

本例中 A2 需要指定兩個(gè)維,type和 id,代碼其它部分與單字段鍵一致。

2.2.1.2 創(chuàng)建索引

A

1

=file("multi.ctx")

2

=A1.create()

3

=A2.index(type_id_idx;type,id;data)

由于本例中有兩個(gè)維,建立索引時(shí)需要包含 type 和 id 兩個(gè)維,如 A3 所示。

2.2.1.3 查詢

A

1

=file("multi.ctx")

2

=A1.create()

3

=[["type_a",55],["type_b",66]]

4

=A2.icursor(;A3.contain([type,id]),type_id_idx)

5

>file("result.txt").export@t(A4)

A3準(zhǔn)備了兩條數(shù)據(jù),是由 type 和 id 構(gòu)成的二元組,作為查找的建值集,結(jié)構(gòu)如下圖所示:

超大數(shù)據(jù)下大批量隨機(jī)鍵值的查詢優(yōu)化方案

A4:A3.contain([type,id]),基于二元組的序列進(jìn)行數(shù)據(jù)的篩選,所以需要將 contain 函數(shù)中的參數(shù)也變?yōu)槎M。

最終導(dǎo)出的結(jié)果文件內(nèi)容如下:

超大數(shù)據(jù)下大批量隨機(jī)鍵值的查詢優(yōu)化方案

2.2.2 方法二(合并主鍵)

雖然多字段鍵可以直接使用,但是涉及到集合的存儲(chǔ)和比較都要慢一些。為了獲取高性能,更常用的辦法是把多字段鍵拼成單字段鍵。

觀察本例數(shù)據(jù)結(jié)構(gòu),雖然 type 是個(gè)串,但卻是可枚舉的,因此可以將 type 數(shù)字化后,與 id 合并為一個(gè)新的主鍵字段。而 long 類型最大值為 2^63-1,完全可以容納 id 和 type 數(shù)字化后的合并結(jié)果。我們把 type 和 id 合并后的新主鍵叫做 nid,可以按數(shù)據(jù)的規(guī)模,確定 nid 中分別用幾位代表 type 和 id。

舉例來(lái)說(shuō),id 的范圍是 9 位數(shù),type 的枚舉個(gè)數(shù)用 3 位數(shù)表示就夠了。因此對(duì)于 nid 而言,需要 13 位(為了避免前幾位是 0,看上去不整齊,我們把第一位數(shù)字設(shè)為 1)。這樣就可以把聯(lián)合主鍵變成單字段的唯一主鍵,去掉第一位后的 12 位數(shù),前 3 位代表數(shù)字化后的 type,后 9 位就是原來(lái)的 id。

代碼如下:


A

1

=["type_a",……,"type_z","type_1",……,"type_9","type_0"]

2

=A1.new(#:tid,~:type)

3

=file("multi_source.txt")

4

=A3.cursor@t()

5

=A4.switch(type,A2:type)

6

=A4.new(1000000000000+type.tid*long(1000000000)+id:nid,data)

7

=A4.skip(99999995)

8

=A4.fetch(10)

A1:type 的枚舉值組成的序列。在實(shí)際情況中,枚舉列表可能來(lái)自文件或者數(shù)據(jù)庫(kù)數(shù)據(jù)源。。

A2:給枚舉值序列中每個(gè) type 一個(gè) tid。為后續(xù)的數(shù)字化主鍵合并做準(zhǔn)備。

A3~A6:從 multi_source.txt 文件中獲取數(shù)據(jù),并按照 A2 中的對(duì)應(yīng)關(guān)系,把 type 列的枚舉串變成數(shù)字,然后將 type 和 id 進(jìn)行合并后,生成新的主鍵 nid。

A7~A8:查看一下合并逐漸后的數(shù)據(jù)情況,跳過(guò)游標(biāo) A4 的前 99999995 條記錄后,取 10 條記錄,結(jié)果如下:

超大數(shù)據(jù)下大批量隨機(jī)鍵值的查詢優(yōu)化方案

這樣就得到了新的“單字段建”的數(shù)據(jù)結(jié)構(gòu):

字段名稱

類型

是否主鍵

說(shuō)明

nid

long

包含 type 和 id 信息的唯一主鍵

data

string


需要獲取的數(shù)據(jù)

接下來(lái)按照 "單字段鍵" 中的做法就可以處理了,當(dāng)然還要注意確保 nid 有序。

2.3 多線程查詢

在上述方法的基礎(chǔ)上,我們還可以采用多線程并行方式來(lái)進(jìn)一步提高性能。

所謂多線程并行,就是把數(shù)據(jù)分成 N 份,用 N 個(gè)線程查詢。但如果只是隨意地將數(shù)據(jù)分成 N 份,很可能無(wú)法真正地提高性能。因?yàn)閷⒁樵兊逆I值集是未知的,所以理論上也無(wú)法確保希望查找的數(shù)據(jù)能夠均勻分布在每一份組表文件中。比較好的處理方式是先觀察鍵值集的特征,從而盡可能地進(jìn)行數(shù)據(jù)的均勻拆分。

比如說(shuō),繼續(xù)使用上文中多字段鍵拼成單字段鍵的例子,將合并后的主鍵 nid 對(duì) 4 取模,余數(shù)相同的數(shù)據(jù)存在同一個(gè)組表中,最終由 4 個(gè)組表文件裝載現(xiàn)有全部數(shù)據(jù)。這樣的文件拆分方法,可以使被查詢的數(shù)據(jù)分布的相對(duì)更加均勻一些。

如果鍵值數(shù)據(jù)有比較明顯的業(yè)務(wù)特征,我們可以考慮按照實(shí)際業(yè)務(wù)場(chǎng)景使用日期、部門之類的字段來(lái)處理文件拆分。如:將屬于部門 A 的 1000 條記錄均分在 10 個(gè)文件中,每個(gè)文件就有 100 條記錄。在利用多線程查詢屬于部門 A 的記錄時(shí),每個(gè)線程就會(huì)從各自對(duì)應(yīng)的文件中取數(shù)相應(yīng)的這 100 條記錄了。

下面我們來(lái)看個(gè)實(shí)際的例子。

2.3.1 創(chuàng)建組表


A

1

=["type_a",……,"type_z","type_1",……,"type_9","type_0"]

2

=A1.new(#:tid,~:type)

3

=file("multi_source.txt")

4

=A3.cursor@t()

5

=A4.switch(type,A2:type)

6

=A4.new(1000000000000+type.tid*long(1000000000)+id:nid,data)

7

=N.(file("nid_"+string(~-1)+"_T.ctx").create(#nid,data))

8

=N.(eval("channel(A4).select(nid%N=="+string(~-1)+").attach(A7("+string(~)+").append(~.cursor()))"))

9

for A6,500000

A1~A6:與多字段鍵的方法二一致。

A7:使用循環(huán)函數(shù),創(chuàng)建名為“鍵值名 _ 鍵值取 N 的余數(shù) _T.ctx”的組表文件,其結(jié)構(gòu)同為 (#nid,data)。

A8:用循環(huán)函數(shù)將游標(biāo)數(shù)據(jù)分別追加到 N 個(gè)原組表上。比如當(dāng) N=1 時(shí),拼出的 eval 函數(shù)參數(shù)為:channel(A4).select(nid%4==0).attach(A7(1).append(~.cursor()))。意思是對(duì)游標(biāo) A4 創(chuàng)建管道,將管道中記錄按鍵值 nid 取 4 的余數(shù),將余數(shù)值等于 0 的記錄過(guò)濾出來(lái)。attach 是對(duì)當(dāng)前管道的附加運(yùn)算,表示取和當(dāng)前余數(shù)值對(duì)應(yīng)的原組表,將當(dāng)前管道中篩選過(guò)濾出的記錄,以游標(biāo)記錄的方式追加到 A7(1),即第 1 個(gè)組表。

A9:循環(huán)游標(biāo) A6,每次獲取 50 萬(wàn)條記錄,直至 A6 游標(biāo)中的數(shù)據(jù)取完。

執(zhí)行后,產(chǎn)出 4(這時(shí)例子取 N=4)個(gè)獨(dú)立的組表文件:

超大數(shù)據(jù)下大批量隨機(jī)鍵值的查詢優(yōu)化方案

2.3.2 創(chuàng)建建索引


A

B

1

fork directory@p("nid*T.ctx")

=file(A1).create().index(nid_idx;nid;data)

A1:列出滿足 nid*T.ctx 的文件名(這里 * 為通配符),這里 @p 選項(xiàng)代表需要返回帶有完整路徑信息的文件名。使用 fork 執(zhí)行多線程時(shí),需要注意環(huán)境中的并行限制數(shù)是否設(shè)置合理。這里用了 4 個(gè)線程,設(shè)計(jì)器中對(duì)應(yīng)的設(shè)置如下:

超大數(shù)據(jù)下大批量隨機(jī)鍵值的查詢優(yōu)化方案

B2:每個(gè)線程為各個(gè)組表建立對(duì)應(yīng)的索引文件,最終結(jié)果如下:

超大數(shù)據(jù)下大批量隨機(jī)鍵值的查詢優(yōu)化方案

2.3.3 查詢


A

B

1

=file("keys.txt").import@i()


2

=A1.group(~%N)


3

fork N.(~-1),A2

=A3(2)

4


=file("nid_"/A3(1)/"_T.ctx").create().icursor(;B3.contain(nid),nid_idx)

5


return B4

6

=A3.conjx()


7

=file("result_nid.txt").export@t(A6)


A1:從 keys.txt 獲取查詢鍵值序列,因?yàn)橹挥幸涣薪Y(jié)果,使用 @i 選項(xiàng),將結(jié)果返回成序列:

超大數(shù)據(jù)下大批量隨機(jī)鍵值的查詢優(yōu)化方案

A2:把 A1 的序列按 4 的余數(shù)進(jìn)行等值分組:

超大數(shù)據(jù)下大批量隨機(jī)鍵值的查詢優(yōu)化方案

A3、B3~B5:用 fork 函數(shù),按等值分組后的鍵值對(duì)各個(gè)組表分別并行查詢。這里的 fork 后面分別寫了兩個(gè)參數(shù),第一個(gè)是循環(huán)函數(shù) N.(~-1),第二個(gè)是 A2。在接下來(lái)的 B3、B4 中分別使用 A3(2) 和 A3(1) 來(lái)獲取 fork 后面這兩個(gè)對(duì)應(yīng)順序的參數(shù),B4:對(duì)組表文件進(jìn)行根據(jù) B3 中的鍵值集進(jìn)行數(shù)據(jù)篩選,B5:返回游標(biāo)。由于 A3 中是多個(gè)線程返回的游標(biāo)序列,所以 A6 中需要使用 conjx 對(duì)多個(gè)游標(biāo)進(jìn)行縱向連接。

A6~A7:將多個(gè)線程返回的游標(biāo)進(jìn)行縱向連接后,導(dǎo)出游標(biāo)記錄至文本文件,前幾行內(nèi)容如下。

超大數(shù)據(jù)下大批量隨機(jī)鍵值的查詢優(yōu)化方案

2.4 數(shù)據(jù)追加

前面我們已經(jīng)解決了針對(duì)大數(shù)據(jù)的批量隨機(jī)鍵值查詢問(wèn)題,不過(guò),我們不能假定數(shù)據(jù)永遠(yuǎn)不變。尤其是對(duì)于大數(shù)據(jù)來(lái)說(shuō),新數(shù)據(jù)的追加是必然要面對(duì)的。在將新數(shù)據(jù)追加到原有組表文件中時(shí),我們需要討論三種情況:有序鍵值追加、無(wú)序鍵值追加,以及數(shù)據(jù)量很大時(shí)的數(shù)據(jù)追加。

2.4.1 有序鍵值追加

單個(gè)文件時(shí),如果鍵值有序,追加的代碼如下:


A

1

=file("single.ctx")

2

=A1.create()

3

=file("single_add.txt")

4

=A3.cursor@t()

5

=A2.append(A4)

A1:single.ctx 是已有的組表文件,結(jié)構(gòu)為 (#id,data),其中 id 為自增鍵值。

A3~A5:新數(shù)據(jù)文件與已有文件結(jié)構(gòu)相同,其 id 加入原組表后,對(duì)于整體數(shù)據(jù)也是有序的。這種情況可以直接追加到原組表,組表會(huì)自動(dòng)更新索引。

如果按按多線程的方法拆分為多個(gè)文件,代碼如下:


A

1

=file("single_add.txt")

2

=A1.cursor@t()

3

=directory@p("id*T.ctx").(file(~).create())

4

=N.(eval("channel(A2).select(id%N=="+string(~-1)+").attach(A3("+string(~)+").append([~.cursor()]))"))

5

for   A2,500000

A1、A2:用游標(biāo)方式獲取新增數(shù)據(jù)。

A3:滿足通配符串:"id*T.ctx",現(xiàn)有 N 份組表文件的序列。

A4、A5:與前面方法中的代碼一致。

2.4.2 無(wú)序鍵值追加

同樣先來(lái)看一下單個(gè)文件的追加方法,以單字段鍵為例,代碼如下:


A

1

=file("single.ctx")

2

=A1.create().cursor()

3

=file("single_add.txt")

4

=A3.cursor@t()

5

=file("single.ctx_temp").create(#id,data)

6

=A5.append([A2,A4].mergex(id))

A2:游標(biāo)方式打開(kāi)現(xiàn)有組表。

A4:游標(biāo)方式獲取新增數(shù)據(jù)。

A5:建個(gè)新的組表文件。

A6:在新的組表中存放現(xiàn)有組表數(shù)據(jù)和新增數(shù)據(jù)歸并后的結(jié)果。這里要注意的是,用 cs.mergex(x) 進(jìn)行歸并操作,需要 cs 序列對(duì) x 有序,也就是要求組表文件 A1 和新增數(shù)據(jù)文件 A3 中的數(shù)據(jù)對(duì)于 id 都分別有序。若不滿足 cs 對(duì) x 有序,程序雖然不會(huì)報(bào)錯(cuò),但是歸并出來(lái)的結(jié)果也是無(wú)序的。

當(dāng)這段代碼執(zhí)行完后,還需要進(jìn)行舊組表、舊索引的清理以及對(duì)新組表的建立索引等操作:


A

1

=movefile(file("single.ctx"))

2

=movefile(file("single.ctx__id_idx"))

3

=movefile(file("single.ctx_temp"),"single.ctx"))

4

=file("single.ctx").create().index(id_idx;id;data)

前三行是文件操作,詳見(jiàn)函數(shù)參考:movefile。A4 為組表建立索引,不再詳述。

下面再看看多個(gè)文件的追加方法,以多字段鍵轉(zhuǎn)單字段鍵后的數(shù)據(jù)結(jié)構(gòu) (nid,data) 為例,代碼如下:


A

1

=["type_a",……,"type_z","type_1",……,"type_9","type_0"]

2

=A1.new(#:tid,~:type)

3

=file("multi_source_add.txt")

4

=A3.cursor@t()

5

=A4.switch(type,A2:type)

6

=A4.new(1000000000000+type.tid*long(1000000000)+id:nid,data)

7

=directory@p("nid*T.ctx").(file(~).create().cursor())

8

=directory@p("nid*T.ctx").(file(~+"_temp").create(#nid,data))

9

=N.(eval("channel(A4).select(nid%N=="+string(~-1)+").attach(A8("+string(~)+").append([~.cursor(),A7("+string(~)+")].mergex(nid)))"))

10

for A4,500000

A3:multi_source_add.txt 是新增數(shù)據(jù)來(lái)源。

A7:假設(shè)原組表已存在,列出原組表的文件名,依次獲取組表游標(biāo),返回成序列。

A8:建立新的組表文件,用來(lái)存放舊組表數(shù)據(jù)和新增數(shù)據(jù),在原有文件名后加上 "_temp",以示區(qū)別。

A9:對(duì)新增數(shù)據(jù)使用管道,將管道中的 N 份游標(biāo)記錄與對(duì)應(yīng)的 N 個(gè)舊份組表中游標(biāo)記錄進(jìn)行歸并,追加到新 N 份組表中。上文已有詳細(xì)的解釋。

當(dāng)這段代碼執(zhí)行完后,還需要進(jìn)行舊組表、舊索引的清理以及對(duì)新組表的索引建立等操作,如下:


A

1

=directory@p("*T.ctx_temp")

2

=A1.(left(~,len(~)-5))

3

=A2.(movefile(file(~)))

4

=A1.(left(~,len(~)-5)+"__nid_idx")

5

=A2.(movefile(file(~)))

6

=A1.(movefile(file(~),left(~,len(~)-5)))

7

=A2.(file(~).create().index(nid_idx;nid;data))

代碼中幾乎全是循環(huán)函數(shù)與簡(jiǎn)單的文件操作。詳見(jiàn)函數(shù)參考《文件》。最后一行建立索引,前文中也已多次解釋。

2.4.3 數(shù)據(jù)量很大時(shí)的數(shù)據(jù)追加

隨著新數(shù)據(jù)不斷增加,每次新追加數(shù)據(jù)與全量歷史數(shù)據(jù)歸并的時(shí)間成本將會(huì)越來(lái)越高。這時(shí)需要把每份組表文件分為新、舊兩份,新的一份是最近一段時(shí)間內(nèi)積累的追加數(shù)據(jù),舊的是之前的歷史數(shù)據(jù)。每當(dāng)有新數(shù)據(jù)需要追加時(shí),還是按 2.4.2 的處理思路操作,但只對(duì)新的那份組表文件進(jìn)行處理。當(dāng)新份數(shù)據(jù)文件超過(guò)一定大小閾值(如 100G),再和舊數(shù)據(jù)合并。這樣的做法不僅可以減少歸并的時(shí)間成本,另一方面也可以降低對(duì)磁盤的損耗。

列舉的數(shù)據(jù)結(jié)構(gòu)還是以 (nid,data) 為例,這次我們從頭開(kāi)始完整地看一遍代碼:

首先定義新、舊組表文件,命名規(guī)則如下:

新份組表:鍵值名 _ 鍵值取 N 的余數(shù) _T.ctx;舊份組表:鍵值名 _ 鍵值取 N 的余數(shù) _H.ctx。

1、 建立新、舊組表,本例中 N=4,代表建立 4 份組表:


A

1

=N.(file("nid_"+string(~-1)+"_H.ctx").create(#nid,data))

2

=N.(file("nid_"+string(~-1)+"_T.ctx").create(#nid,data))

N  取 4,生成的歷史和臨時(shí)組表文件如下:  

超大數(shù)據(jù)下大批量隨機(jī)鍵值的查詢優(yōu)化方案

2、   在新組表上追加新數(shù)據(jù)。  


A

1

=["type_a",……,"type_z","type_1",……,"type_9","type_0"]

2

=A1.new(#:tid,~:type)

3

=file("multi_source_.txt")

4

=A3.cursor@t()

5

=A4.switch(type,A2:type)

6

=A4.new(1000000000000+type.tid*long(1000000000)+id:nid,data)

7

=directory@p("nid*T.ctx").(file(~).create().cursor())

8

=directory@p("nid*T.ctx").(file(~+"_temp").create(#nid,data))

9

=N.(eval("channel(A4).select(nid%N=="+string(~-1)+").attach(A8("+string(~)+").append([~.cursor(),A7("+string(~)+")].mergex(nid)))"))

10

for A4,500000

3、   新組表合并后,清理原來(lái)的新組表和索引,然后重建新組表索引。  


A

1

=directory@p("*T.ctx_temp")

2

=A1.(left(~,len(~)-5))

3

=A2.(movefile(file(~)))

4

=A1.(left(~,len(~)-5)+"__nid_idx")

5

=A2.(movefile(file(~)))

6

=A1.(movefile(file(~),left(~,len(~)-5)))

7

=A2.(file(~).create().index(nid_idx;nid;data))

4、   對(duì)新數(shù)據(jù)大小進(jìn)行判斷,如果超過(guò)參數(shù) B(單位是字節(jié)數(shù))則與舊份組表數(shù)據(jù)合并。  


A

B

C

1

fork directory@p("nid*T.ctx")

=file(A1)


2


if B1.size()>B

=left(A1,(len(A1)-5))+"H.ctx"

3



=B1.create().cursor()

4



=file(C2).create().cursor()

5



=left(A1,(len(A1)-5))+"H.ctx_temp"

6



=file(C5).create(#nid,data).append([C3,C4].mergex(nid))

5、   舊組表與新組表合并后,清理原來(lái)的舊組表和索引,然后重建舊組表索引。清理已合并的新組表,并重建空的新組表。  


A

1

=directory@p("*H.ctx_temp")

2

=A1.(left(~,len(~)-5))

3

=A2.(movefile(file(~)))

4

=A1.(left(~,len(~)-5)+"__nid_idx")

5

=A4.(movefile(file(~)))

6

=A1.(movefile(file(~),left(~,len(~)-5)))

7

=A2.(file(~).create().index(nid_idx;nid;data))

8

=A1.(left(~,len(~)-10)+"T.ctx")

9

=A8.(movefile(file(~)))

10

=A1.(left(~,len(~)-10)+"T.ctx__nid_idx")

11

=A10.(movefile(file(~)))

12

=A8.(file(~).create(#nid,data))

6、   對(duì)新、舊組表文件分別利用多線程進(jìn)行查詢  


A

B

1

=file("keys.txt").import@i()


2

=A1.group(~%N)


3

fork   directory@p("*H.ctx"),directory@p("*T.ctx"),A2

=A3(3)

4


=file(A3(1)).create().icursor(;B3.contain(nid),nid_idx)

5


=file(A3(2)).create().icursor(;B3.contain(nid),nid_idx)

6


return B4|B5

7

=A3.conj()


8

=file("result.txt").export@t(A8)


這里需要注意 A7 中寫法,因?yàn)?B6 中返回 B4|B5,所以導(dǎo)致 A3 的結(jié)果為多個(gè)游標(biāo)序列的序列,因此在對(duì) A3 進(jìn)行縱向連接時(shí),應(yīng)該使用序列的 conj,而不是游標(biāo)的 conjx。

至此,基于本文的 6 個(gè)集算器腳本文件,在第三方定時(shí)任務(wù)調(diào)度工具的合理調(diào)用下,可以實(shí)現(xiàn)單機(jī)情況下大數(shù)據(jù)量數(shù)據(jù)的追加,以及針對(duì)批量隨機(jī)鍵值的查詢工作。


新聞標(biāo)題:超大數(shù)據(jù)下大批量隨機(jī)鍵值的查詢優(yōu)化方案
文章分享:http://muchs.cn/article36/jcpspg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站維護(hù)、全網(wǎng)營(yíng)銷推廣、微信公眾號(hào)、網(wǎng)頁(yè)設(shè)計(jì)公司、建站公司、網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

搜索引擎優(yōu)化