日常生活中,我們會(huì)遇到各種各樣的數(shù)據(jù),小到公司通訊錄,大到互聯(lián)網(wǎng)用戶行為分析。在進(jìn)行數(shù)據(jù)分析處理的過(guò)程中,查詢是必不可少的環(huán)節(jié),如何更加高效地進(jìn)行數(shù)據(jù)查詢。
創(chuàng)新互聯(lián)專注于企業(yè)營(yíng)銷型網(wǎng)站建設(shè)、網(wǎng)站重做改版、烏蘇網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、H5頁(yè)面制作、商城建設(shè)、集團(tuán)公司官網(wǎng)建設(shè)、成都外貿(mào)網(wǎng)站建設(shè)、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁(yè)設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為烏蘇等各大城市提供網(wǎng)站開(kāi)發(fā)制作服務(wù)。SPL為用戶提供了強(qiáng)大的索引機(jī)制以及針對(duì)不同場(chǎng)景中各對(duì)象的查詢函數(shù),善加運(yùn)用,可以顯著提高查詢性能。
我們先建立一個(gè)份“通話記錄”的模擬數(shù)據(jù),通過(guò)這份數(shù)據(jù),來(lái)比較一下不同查詢函數(shù)對(duì)序表查詢性能的影響。建立模擬數(shù)據(jù)的代碼如下:
A | |
1 | =5000000.new(13800000000+~:Subscriber,datetime(1547123785410+rand(864000000)):Time,rand(3600)+1:Length,rands("TF",1):isLocal) |
2 | =file("btx/voiceBill.btx").export@b(A1) |
代碼1.1.1
其中部分?jǐn)?shù)據(jù)如下:
圖1.1.1
對(duì)序表進(jìn)行查詢,通常我們會(huì)想到使用A.select()函數(shù)。我們來(lái)看一下使用該函數(shù)的效果:
A | B | |
1 | =file("btx/voiceBill.btx").import@b() | /讀取文件中的數(shù)據(jù),作為序表 |
2 | =now() | /當(dāng)前時(shí)間 |
3 | =A1.select(Subscriber==13800263524) | /使用A.select()函數(shù)進(jìn)行查詢 |
4 | =interval@ms(A2,now()) | /查詢耗時(shí) |
代碼1.1.2
查詢耗時(shí)為80毫秒。
對(duì)序表的鍵值進(jìn)行查詢時(shí),可以利用A.find()函數(shù)進(jìn)行查詢。示例代碼如下:
A | B | |
1 | =file("btx/voiceBill.btx").import@b() | /讀取文件中的數(shù)據(jù),作為序表 |
2 | >A1.keys(Subscriber) | /設(shè)置Subscriber為主鍵 |
3 | >A1.index() | /建立索引 |
4 | =now() | /當(dāng)前時(shí)間 |
5 | =A1.find(13800263524) | /使用A.find()函數(shù)查找 |
6 | =interval@ms(A4,now()) | /查詢花費(fèi)的時(shí)間 |
代碼1.1.3
查詢耗時(shí)為1毫秒。
這是因?yàn)樵诩闫鞯男虮碇?,可以指定某個(gè)或某些字段作為主鍵,基于主鍵的查找可以使用專門(mén)的函數(shù)。比如代碼1.1.3中A5的find函數(shù),不僅能簡(jiǎn)化書(shū)寫(xiě),更能有效地提高計(jì)算性能。
當(dāng)鍵值較多時(shí),我們使用函數(shù)A.find@k ()進(jìn)行批量鍵值查找。示例代碼如下:
A | B | |
1 | =file("btx/voiceBill.btx").import@b() | /讀取文件中的數(shù)據(jù),作為序表 |
2 | >A1.keys(Subscriber) | /設(shè)置Subscriber為主鍵 |
3 | >A1.index() | /建立索引 |
4 | =A1(50.(rand(A1.len())+1)).(Subscriber) | /從Subscriber中隨機(jī)取50個(gè)值 |
5 | =now() | /當(dāng)前時(shí)間 |
6 | =A1.find@k(A4) | /使用A.find@k()函數(shù)對(duì)A4進(jìn)行批量鍵值查找 |
7 | =interval@ms(A5,now()) | /查詢花費(fèi)的時(shí)間 |
代碼1.1.4
要注意的是,在使用A.find()函數(shù)時(shí),需事先建立主鍵,否則會(huì)報(bào)“缺少主鍵”的錯(cuò)誤。
利用主鍵值查找的函數(shù),可以有效地提升計(jì)算性能,是由于在序表中為主鍵建立索引表。在代碼1.1.4中,未建立索引時(shí),平均查詢時(shí)間在1400毫秒左右;建立索引后,查詢平均耗時(shí)不到1毫秒。
序表中的數(shù)據(jù)量越大,需要查找的次數(shù)越多,對(duì)效率的提升就越明顯。
當(dāng)查詢條件對(duì)應(yīng)多個(gè)鍵時(shí),示例代碼如下:
A | B | |
1 | =file("btx/voiceBill.btx").import@b() | /讀取文件中的數(shù)據(jù),作為序表 |
2 | >A1.keys(Subscriber,isLocal) | /設(shè)置Subscriber,isLocal為主鍵 |
3 | >A1.index() | /建立索引 |
4 | =[[13800000002,"F"],[13802568478,"F"]] | /鍵有多個(gè),因此組成序列 |
5 | =now() | /當(dāng)前時(shí)間 |
6 | =A1.find@k(A4) | /使用A.find@k()函數(shù)對(duì)A4進(jìn)行批量鍵值查找 |
7 | =interval@ms(A5,now()) | /查詢花費(fèi)的時(shí)間 |
代碼1.1.5
switch/join函數(shù)同樣需要根據(jù)主鍵值在序表中查找記錄,使用時(shí)會(huì)對(duì)維表自動(dòng)建立索引。若在多線程fork函數(shù)之前沒(méi)有對(duì)相應(yīng)維表建立索引,就會(huì)在每個(gè)線程中都自動(dòng)為該維表建立一個(gè)索引,執(zhí)行過(guò)程中會(huì)消耗更多內(nèi)存,這樣有可能會(huì)造成內(nèi)存溢出,如圖1.1.1.2,要注意避免,較好的處理方式可以參考圖?1.1.3。
圖?1.1.2 fork中的每個(gè)線程都自動(dòng)建立了索引導(dǎo)致內(nèi)存溢出
圖?1.1.3 fork執(zhí)行前,先對(duì)維表建立索引
對(duì)有序的集文件進(jìn)行查找,可以使用f.iselect()函數(shù)實(shí)現(xiàn)二分查找,該函數(shù)也支持批量查找,下面是個(gè)基于集文件使用f.iselect()批量查找的例子:
A | |
1 | =5000000.new(13800000000+~:Subscriber,datetime(1547123785410+rand(864000000)):Time,rand(3600)+1:Length,rands("TF",1):isLocal) |
2 | =file("btx/voiceBill@z.btx").export@z(A1) |
代碼1.2.1
代碼1.2.1,建立集文件voiceBill@z.btx。顯然,Subscriber是有序的。
A | B | |
1 | =file("btx/voiceBill@z.btx") | |
2 | =50.(13800000000+rand(5000000)+1).id() | /從500萬(wàn)個(gè)Subscriber中隨機(jī)找50個(gè),去重并排序 |
3 | =now() | /當(dāng)前時(shí)間 |
4 | =A1.iselect@b(A2,Subscriber) | /對(duì)集文件使用f.iselect()二分查找函數(shù)進(jìn)行批量查找 |
5 | =A4.fetch() | |
6 | =interval@ms(A3,now()) | /查詢花費(fèi)的時(shí)間 |
代碼1.2.2
代碼1.2.2,因?yàn)閒.iselect()是個(gè)二分查找函數(shù),所以需要注意代碼中的A2作為查詢序列,與集文件的編號(hào)一樣,都需要有序。還要注意,這里的選項(xiàng)@b不是二分法的意思,而是讀取通過(guò)f.export()函數(shù)導(dǎo)出的集文件。該集文件導(dǎo)出時(shí),注意需要使用選項(xiàng)@z,否則在使用f.iselect ()對(duì)集文件進(jìn)行查詢時(shí)會(huì)報(bào)錯(cuò)。
假設(shè)數(shù)據(jù)總量為N,使用二分法進(jìn)行查找的時(shí)間復(fù)雜度為logN(以 2 為底),當(dāng)數(shù)據(jù)量越大,性能提升也就越明顯。
組表也有類似序表的T.find()和T.find@k()函數(shù),可以高效地實(shí)現(xiàn)鍵值查找。適合于在大維表中找出少量記錄的場(chǎng)景。我們來(lái)看這樣一個(gè)例子:
A | B | |
1 | =file("ctx/voiceBill.ctx").create(#Subscriber,Time,Length,isLocal) | |
2 | for 5000 | =to((A2-1)*10000+1,A2*10000).new(13800000000+~:Subscriber,datetime(1547123785410+rand(864000000)):Time,rand(3600)+1:Length,rands("TF",1):isLocal) |
3 | =A1.append(B2.cursor()) |
代碼1.3.1
代碼1.3.1,建立組表文件voiceBill.ctx,其中Subscriber是該組表的維。
A | B | |
1 | =file("ctx/voiceBill.ctx").create() | /打開(kāi)組表 |
2 | =13801701672 | /組表數(shù)據(jù)其中的一個(gè)Subscriber值 |
3 | =now() | /當(dāng)前時(shí)間 |
4 | =A1.cursor().select(Subscriber==A2).fetch() | /使用cs.select()對(duì)組表進(jìn)行查詢 |
5 | =interval@ms(A3,now()) | /查詢花費(fèi)的時(shí)間 |
代碼1.3.2
代碼1.3.2,對(duì)組表使用cs.select()函數(shù)進(jìn)行查詢,耗時(shí)為:13855毫秒。
A | B | |
1 | =file("ctx/voiceBill.ctx").create() | /打開(kāi)組表 |
2 | =13801701672 | /組表數(shù)據(jù)其中的一個(gè)Subscriber值 |
3 | =now() | /當(dāng)前時(shí)間 |
4 | =A1.find(A2) | /使用T.find()對(duì)組表進(jìn)行查詢 |
5 | =interval@ms(A3,now()) | /查詢花費(fèi)的時(shí)間 |
代碼1.3.3
代碼1.3.3,對(duì)組表使用T.find()函數(shù)進(jìn)行查詢,耗時(shí)為:77毫秒。
對(duì)比可見(jiàn):對(duì)于有維的組表,可以使用類似序表的T.find()函數(shù),進(jìn)行單個(gè)或者批量鍵值的查詢,其查詢效率遠(yuǎn)高于從篩選后的游標(biāo)中取數(shù)。
組表上可以建立三種索引,每種索引針對(duì)的情況也不同,分別為:
1、? hash索引,適合單值查找,比如枚舉類型;
2、? 排序索引,適合區(qū)間查找,比如數(shù)字、日期、時(shí)間類型;
3、? 全文索引,用于模糊查詢,比如字符串類型。
下面我們來(lái)建立一個(gè)組表,使其數(shù)據(jù)類型覆蓋以上三種索引,如下:
A | B | |
1 | =file("ctx/voiceBillDetail.ctx").create(#Subscriber,Time,Length,isLocal,City,Company) | |
2 | =file("info/city_en.txt").import@i() | =A2.len() |
3 | for 5000 | =to((A3-1)*10000+1,A3*10000).new(13800000000+~:Subscriber,datetime(1547123785410+rand(864000000)):Time,rand(3600)+1:Length,rands("TF",1):isLocal,A2(rand(B2)+1):City,rands("ABCDEFGHIJKLMNOPQRSTUVWXYZ",14)+"? Co. Ltd":Company) |
4 | =A1.append(B3.cursor()) |
代碼2.1
代碼2.1建立的組表,前十條記錄如下:
圖2.1
A | B | |
1 | =file("ctx/voiceBillDetail.ctx").create() | /打開(kāi)組表 |
2 | =A1.index(subscriber_idx;Subscriber) | /用戶手機(jī)號(hào)碼,數(shù)值用排序索引 |
3 | =A1.index(time_idx;Time) | /通話開(kāi)始時(shí)間,數(shù)值用排序索引 |
4 | =A1.index(length_idx;Length) | /通話時(shí)長(zhǎng),數(shù)值用排序索引 |
5 | =A1.index(city_idx:1;City) | /城市,枚舉用hash索引 |
6 | =A1.index@w(company_idx;Company) | /公司,字串用全文索引 |
代碼2.2
代碼2.2,根據(jù)每列數(shù)據(jù)類型的特點(diǎn),建立不同類型的索引。建立好的索引和組表文件如圖2.2:
圖2.2
集算器能自動(dòng)識(shí)別條件找到合適的索引,等值和區(qū)間都可以,like(“A*”)式的也支持。我們來(lái)看下效果:
A | B | |
1 | =file("ctx/voiceBillDetail.ctx").create() | /打開(kāi)組表 |
2 | =now() | /當(dāng)前時(shí)間 |
3 | =A1.icursor(;Subscriber==13834750766,subscriber_idx).fetch() | /icursor查詢 |
4 | =interval@ms(A2,now()) | /查詢耗時(shí) |
代碼2.3
A | B | |
1 | =file("ctx/voiceBillDetail.ctx").create() | /打開(kāi)組表 |
2 | =now() | /當(dāng)前時(shí)間 |
3 | =A1.icursor(;Subscriber==13834750766).fetch() | /icursor查詢 |
4 | =interval@ms(A2,now()) | /查詢耗時(shí) |
代碼2.4
代碼2.3是沒(méi)有省略索引名稱的寫(xiě)法,代碼2.4是省略索引名稱的寫(xiě)法。兩者時(shí)間消耗基本相同,都是100毫秒左右。
A | B | |
1 | =file("ctx/voiceBillDetail.ctx").create() | /打開(kāi)組表 |
2 | =now() | /當(dāng)前時(shí)間 |
3 | =A1.cursor().select(Subscriber==13834750766).fetch() | /普通游標(biāo)查詢 |
4 | =interval@ms(A2,now()) | /查詢耗時(shí) |
代碼2.5
代碼2.5使用普通游標(biāo)查詢同樣的記錄,查詢耗時(shí)則需要40秒左右。
A | B | |
1 | =file("ctx/voiceBillDetail.ctx").create() | /打開(kāi)組表 |
2 | =now() | /當(dāng)前時(shí)間 |
3 | =A1.icursor(;Subscriber>=13834750766 && ? Subscriber<=13834750780).fetch() | /icursor查詢 |
4 | =interval@ms(A2,now()) | /查詢耗時(shí) |
代碼2.6
代碼2.6對(duì)Subscriber使用排序索引,進(jìn)行區(qū)間查找,查詢耗時(shí)是70毫秒左右。
A | B | |
1 | =file("ctx/voiceBillDetail.ctx").create() | /打開(kāi)組表 |
2 | =now() | /當(dāng)前時(shí)間 |
3 | =A1.cursor().select(Subscriber>=13834750766 ? && Subscriber<=13834750780).fetch() | /普通游標(biāo)查詢 |
4 | =interval@ms(A2,now()) | /查詢耗時(shí) |
代碼2.7
代碼2.7使用普通游標(biāo)查詢同樣條件的記錄,查詢耗時(shí)則需要40秒左右。
A | B | |
1 | =file("ctx/voiceBillDetail.ctx").create() | /打開(kāi)組表 |
2 | =now() | /當(dāng)前時(shí)間 |
3 | =A1.icursor(like(Company,"*ABCDE*")).fetch() | /icursor查詢 |
4 | =interval@ms(A2,now()) | /查詢耗時(shí) |
代碼2.8
代碼2.8對(duì)Company使用全文索引,進(jìn)行模糊查詢,查詢耗時(shí)是1500毫秒左右。
A | B | |
1 | =file("ctx/voiceBillDetail.ctx").create() | /打開(kāi)組表 |
2 | =now() | /當(dāng)前時(shí)間 |
3 | =A1.cursor().select(like(Company,"*ABCDE*")).fetch() | /普通游標(biāo)查詢 |
4 | =interval@ms(A2,now()) | /查詢耗時(shí) |
代碼2.9
代碼2.9使用普通游標(biāo)查詢同樣條件的記錄,查詢耗時(shí)則需要40秒左右。
當(dāng)數(shù)據(jù)規(guī)模更大時(shí),例如:
A | B | |
1 | =file("ctx/employee.ctx") | |
2 | =A1.create(#id,name,sex,city,birthday,salary,level,height,weight,company) | |
3 | =file("info/ming_en_female.txt").import@i() | =A3.len() |
4 | =file("info/ming_en_male.txt").import@i() | =A4.len() |
5 | =file("info/xing_en.txt").import@i() | =A5.len() |
6 | =city=file("info/city_en.txt").import@i() | =A6.len() |
7 | =salary=20000 | /10000~30000 |
8 | =["one","two","three","four","five","six","seven","eight","nine","ten"] | =A8.len() |
9 | =height=50 | /160~210cm |
10 | =weight=50 | /50~100kg |
11 | =birthtime=946656 | /1970~2000 |
12 | for 10000 | =to((A12-1)*100000+1,A12*100000).new(~:id,if(rand(2)==0,A3(rand(B3)+1),A4(rand(B4)+1))+"?"+A5(rand(B5)+1):name,if(A3.pos(name.array("")(1)),"Female","Male"):sex,A6(rand(B6-1)+1):city,date(rand(birthtime)*long(1000000)):birthday,rand(salary)+10000:salary,A8(rand(B8-1)+1):level,rand(height)+160:height,rand(weight)+50:weight,if(rand(2)==0,A3(rand(B3)+1),A4(rand(B4)+1))+"&"+A5(rand(B5)+1)+" ? Co. Ltd":company) |
13 | =A2.append(B12.cursor()) |
代碼2.10
代碼2.10,建造了10億條結(jié)構(gòu)如圖2.3的組表文件employee.ctx。
圖2.3
A | B | |
1 | =file("ctx/employee.ctx").create() | /打開(kāi)組表 |
2 | =A1.index(id_idx;id) | /編號(hào),數(shù)值用排序索引 |
3 | =A1.index@w(name_idx;name) | /姓名,字串用全文 |
4 | =A1.index(city_idx:1;city) | /城市,枚舉用hash索引 |
5 | =A1.index(birthday;birthday) | /生日,日期用排序索引 |
6 | =A1.index(salary_idx;salary) | /工資,數(shù)值用排序索引 |
7 | =A1.index(height_idx;height) | /身高,數(shù)值用排序索引 |
8 | =A1.index(weight_idx;weight) | /體重,數(shù)值用排序索引 |
9 | =A1.index@w(company;company) | /公司,字串用全文索引 |
代碼2.11
代碼2.11中,對(duì)大部分列建立了索引。組表與索引的各個(gè)文件如圖2.4。
圖2.4
多等值條件項(xiàng)&&時(shí),可以分別為每個(gè)字段建立索引。集算器能夠快速在多個(gè)索引中用歸并算法計(jì)算交集。比如:
A | B | |
1 | =file("ctx/employee.ctx").create() | /打開(kāi)組表 |
2 | =now() | /當(dāng)前時(shí)間 |
3 | =A1.icursor(;city=="Casper" ? && salary==25716).fetch() | /icursor查詢多個(gè)等值條件 |
4 | =interval@ms(A2,now()) | /查詢耗時(shí) |
代碼2.12
代碼2.12,查詢條件均為等值查詢,A3查出記錄數(shù)為324條,耗時(shí)31883毫秒。
但區(qū)間條件時(shí)不能再用歸并計(jì)算交集,集算器將只對(duì)其中一個(gè)條件使用索引,另一個(gè)條件使用遍歷計(jì)算,效果就會(huì)差,比如:
A | B | |
1 | =file("ctx/employee.ctx").create() | /打開(kāi)組表 |
2 | =now() | /當(dāng)前時(shí)間 |
3 | =A1.icursor(;height>208 && ? weight<70? && ? salary==21765).fetch() | /icursor查詢多個(gè)區(qū)間條件 |
4 | =interval@ms(A2,now()) | /查詢耗時(shí) |
代碼2.13
代碼2.13,查詢條件均為區(qū)間條件,A3查出記錄數(shù)為389條,耗時(shí)70283毫秒。
組表索引提供了兩級(jí)緩存機(jī)制,可以用index@2或者index@3預(yù)先把索引的索引讀入內(nèi)存,如果需要重復(fù)多次使用索引查找,則可以有效提高性能。
選項(xiàng)@2、@3的意思分別是將索引的第二、三級(jí)緩存先加載進(jìn)內(nèi)存。經(jīng)過(guò)索引緩存的預(yù)處理,第一遍查詢時(shí)間也能達(dá)到查詢數(shù)百次后才能達(dá)到的極限值。@2相比@3緩存的內(nèi)容少,效果相對(duì)差一點(diǎn),但內(nèi)存占用也更少。使用時(shí)需要程序員根據(jù)具體場(chǎng)景來(lái)權(quán)衡@2還是@3。
A | B | |
1 | =file("ctx/employee.ctx").create() | /打開(kāi)組表 |
2 | =now() | /當(dāng)前時(shí)間 |
3 | =A1.icursor(;city=="Casper" ? && salary==25716).fetch() | /icursor查詢 |
4 | =interval@ms(A2,now()) | /查詢耗時(shí) |
代碼3.1
代碼3.1,基于代碼2.10建造的組表文件,不使用索引緩存,查詢耗時(shí)為31883毫秒。
A | B | |
1 | =file("ctx/employee.ctx").create() | /打開(kāi)組表 |
2 | =A1.index@3(city_idx) | /使用第三級(jí)緩存 |
3 | =A1.index@3(salary_idx) | /使用第三級(jí)緩存 |
4 | =now() | /當(dāng)前時(shí)間 |
5 | =A1.icursor(;city=="Casper" ? && salary==25716).fetch() | /icursor查詢 |
6 | =interval@ms(A4,now()) | /查詢耗時(shí) |
代碼3.2
代碼3.2使用第三級(jí)索引緩存,查詢耗時(shí)為5225毫秒。
這里使用的是列存組表,列存采用了數(shù)據(jù)分塊并壓縮的算法,對(duì)于遍歷運(yùn)算來(lái)講,訪問(wèn)數(shù)據(jù)量會(huì)變小,也就會(huì)具有更好的性能。但對(duì)于基于索引隨機(jī)取數(shù)的場(chǎng)景,由于要有額外的解壓過(guò)程,而且每次取數(shù)都會(huì)針對(duì)整個(gè)分塊,運(yùn)算復(fù)雜度會(huì)高很多。因此,從原理上分析,這時(shí)候的性能應(yīng)當(dāng)會(huì)比行存要差。將組表轉(zhuǎn)為行存后,查詢耗時(shí)僅為1592毫秒。
索引緩存在并行時(shí)可以復(fù)用,如下:
A | B | |
1 | =file("ctx/employee.ctx").create() | /打開(kāi)組表 |
2 | =A1.index@3(city_idx) | /使用第三級(jí)緩存 |
3 | =A1.index@3(salary_idx) | /使用第三級(jí)緩存 |
4 | =now() | /當(dāng)前時(shí)間 |
5 | fork [22222,23333,24444,25555] | =A1.icursor(;city=="Casper" ? && salary==A5).fetch() |
6 | return B5 | |
7 | =A5.conj() | /合并并行結(jié)果 |
8 | =interval@ms(A4,now()) | /并行查詢耗時(shí) |
代碼3.3
代碼3.3,并行時(shí),A5的每個(gè)線程中都可以使用A2、A3中建立的第三級(jí)索引緩存,最終查詢耗時(shí)為21376毫秒。
組表的行存和列存形式都支持索引,列存索引查找比行存性能差,返回結(jié)果集較少時(shí)差異不明顯,大量返回時(shí)會(huì)有明顯劣勢(shì),在設(shè)計(jì)存儲(chǔ)方案時(shí)要權(quán)衡。
A | B | |
1 | 1234567890qwertyuiopasdfghjklzxcvbnm | |
2 | =file("id_600m.ctx") | |
3 | =A2.create(#id,data) | |
4 | for 6000 | =to((A4-1)*100000+1,A4*100000).new(~+1000000000000:id,rands(A1,180):data) |
5 | =A3.append(B4.cursor()) |
代碼4.1
代碼4.1建立組表文件id_600m.ctx,結(jié)構(gòu)為(#id,data) ,包含6億條記錄,其中:
A1:包含 26 個(gè)英文字母和 10 個(gè)阿拉伯?dāng)?shù)字的字符串。
A2、A3:建立結(jié)構(gòu)為 (id,data) 的組表文件,使用列式存儲(chǔ)方式。
A4:循環(huán) 6000 次,循環(huán)體B4、B5,每次生成 10 萬(wàn)條對(duì)應(yīng)結(jié)構(gòu)的記錄,并追加到組表文件。
執(zhí)行后,生成組表文件:id_600m.ctx
A | |
1 | =file("id_600m.ctx") |
2 | =A1.create().index(id_idx;id) |
代碼4.2
代碼4.2為組表id列建立索引。
執(zhí)行后,生成組表的索引文件:id_600m.ctx__id_idx。
列存組表生成時(shí) create() 函數(shù)加上 @r 選項(xiàng),即可變?yōu)樯尚写娼M表,其余代碼無(wú)異,這里不再舉例,當(dāng)返回?cái)?shù)據(jù)量較大時(shí):
A | B | |
1 | =10000.(rand(600000000)+1000000000001).id() | /根據(jù)數(shù)據(jù)特點(diǎn),從六億中隨機(jī)取一萬(wàn)個(gè)待查詢的測(cè)試id鍵值并去重 |
2 | =file("id_600m.ctx").create() | /打開(kāi)列存組表 |
3 | =now() | /當(dāng)前時(shí)間 |
4 | =A2.icursor(;A1.contain(id),id_idx).fetch() | /icursor查詢A1中對(duì)應(yīng)的id鍵值 |
5 | =interval@ms(A3,now()) | /列存查詢耗時(shí) |
6 | =file("id_600m@r.ctx").create() | /打開(kāi)行存組表 |
7 | =now() | /當(dāng)前時(shí)間 |
8 | =A6.icursor(;A1.contain(id),id_idx).fetch() | /icursor查詢A1中對(duì)應(yīng)的id鍵值 |
9 | =interval@ms(A7,now()) | /行存查詢耗時(shí) |
代碼4.3
代碼4.3中,列存查詢耗時(shí)和行存查詢耗時(shí),也就是A5和A9的值分別為205270和82800毫秒。
組表支持一種帶值索引,即把查找字段也寫(xiě)入索引,這樣可以不再訪問(wèn)原組表即返回結(jié)果。但存儲(chǔ)空間會(huì)占用較多。
基于代碼4.1的列存組表文件id_600m.ctx。
A | |
1 | =file("id_600m.ctx") |
2 | =A1.create().index(id_data_idx;id;data) |
代碼4.4
代碼4.4為組表id列建立索引,在對(duì)組表建立索引時(shí),當(dāng) index 函數(shù)有數(shù)據(jù)列名參數(shù),如本例 A2 中的 data,就會(huì)在建索引時(shí)把數(shù)據(jù)列 data 復(fù)制進(jìn)索引。當(dāng)有多個(gè)數(shù)據(jù)列時(shí),可以寫(xiě)為:index(id_idx;id;data1,data2,…)。
因?yàn)樵谒饕凶隽巳哂?,索引文件也自然?huì)較大,本文中測(cè)試的列存組表和索引冗余后的文件大小為:
類型 | 文件名 | 大小 |
組表 | id_600m.ctx | 105G |
索引 | id_600m.ctx__id_data_idx | 112G |
當(dāng)數(shù)據(jù)復(fù)制進(jìn)索引后,實(shí)際上讀取時(shí)不再訪問(wèn)原數(shù)據(jù)文件了。
從 6 億條數(shù)據(jù)總量中取 1 萬(wàn)條批量隨機(jī)鍵值,完整的測(cè)試結(jié)果對(duì)比:
耗時(shí)(毫秒) | |||||
單線程 | 多線程(10 線程) | ||||
Oracle | 行存索引 | 冗余索引 | Oracle | 行存索引 | 冗余索引 |
117322 | 20745 | 19873 | 39549 | 10975 | 9561 |
組表索引能夠識(shí)別出contain式條件,支持批量等值查找。
A | B | |
1 | 1234567890qwertyuiopasdfghjklzxcvbnm | |
2 | =file("id_600m.ctx") | |
3 | =A2.create@r(#id,data) | |
4 | for 6000 | =to((A4-1)*100000+1,A4*100000).new(~+1000000000000:id,rands(A1,180):data) |
5 | =A3.append(B4.cursor()) |
代碼5.1
代碼5.1建立組表文件id_600m.ctx,結(jié)構(gòu)為(#id,data) ,包含6億條記錄,其中:
A1:包含 26 個(gè)英文字母和 10 個(gè)阿拉伯?dāng)?shù)字的字符串。
A2、A3:建立結(jié)構(gòu)為 (id,data) 的組表文件,@r 選項(xiàng)表示使用行式存儲(chǔ)方式。
A4:循環(huán) 6000 次,循環(huán)體B4、B5,每次生成 10 萬(wàn)條對(duì)應(yīng)結(jié)構(gòu)的記錄,并追加到組表文件。
執(zhí)行后,生成組表文件:id_600m.ctx。
A | |
1 | =file("id_600m.ctx ? ?") |
2 | =A1.create().index(id_idx;id) |
代碼5.2
代碼5.2為組表id列建立索引。
執(zhí)行后,生成組表的索引文件:id_600m.ctx__id_idx
A | B | |
1 | =file("id_600m.ctx").create() | /打開(kāi)組表 |
2 | =now() | /當(dāng)前時(shí)間 |
3 | =A1.index@3(id_idx) | /使用index@3加載索引緩存 |
4 | =interval@ms(A2,now()) | /加載索引緩存的時(shí)間 |
5 | =10000.(1000000000000+(rand(600000000)+1)).sort() | /隨機(jī)取一些鍵值并排序 |
6 | =now() | /當(dāng)前時(shí)間 |
7 | =A1.icursor(A5.contain(id),id_idx).fetch() | /使用icursor函數(shù)進(jìn)行批量鍵值查詢 |
8 | =interval@ms(A6,now()) | /查詢耗時(shí) |
代碼5.3
代碼5.3,在組表的 icursor()這個(gè)函數(shù)中,使用索引 id_idx,以條件 A2.contain(id) 來(lái)過(guò)濾組表。集算器會(huì)自動(dòng)識(shí)別出 A2.contain(id) 這個(gè)條件可以使用索引,并會(huì)自動(dòng)將 A2 的內(nèi)容排序后從前向后查找。
進(jìn)階使用
使用排序索引多線程查找時(shí),按鍵值排序分組后扔給多個(gè)線程去查詢,避免兩個(gè)線程中有交叉內(nèi)容。同時(shí),還可以設(shè)計(jì)成多個(gè)組表,把鍵值能平均分配到多個(gè)組表上并行查找。
所謂多線程并行,就是把數(shù)據(jù)分成 N 份,用 N 個(gè)線程查詢。但如果只是隨意地將數(shù)據(jù)分成 N 份,很可能無(wú)法真正地提高性能。因?yàn)閷⒁樵兊逆I值集是未知的,所以理論上也無(wú)法確保希望查找的數(shù)據(jù)能夠均勻分布在每一份組表文件中。比較好的處理方式是先觀察鍵值集的特征,從而盡可能地進(jìn)行數(shù)據(jù)的均勻拆分。
如果鍵值數(shù)據(jù)有比較明顯的業(yè)務(wù)特征,我們可以考慮按照實(shí)際業(yè)務(wù)場(chǎng)景使用日期、部門(mén)之類的字段來(lái)處理文件拆分。如:將屬于部門(mén) A 的 1000 條記錄均分在 10 個(gè)文件中,每個(gè)文件就有 100 條記錄。在利用多線程查詢屬于部門(mén) A 的記錄時(shí),每個(gè)線程就會(huì)從各自對(duì)應(yīng)的文件中取數(shù)相應(yīng)的這 100 條記錄了。
下面我們來(lái)看個(gè)實(shí)際的例子,已有數(shù)據(jù)文件multi_source.txt的結(jié)構(gòu)如下:
字段名稱 | 類型 | 說(shuō)明 |
type | string | 可枚舉 |
Id | long | 每個(gè)枚舉類型的 id 都從 1 開(kāi)始自增 |
data | string | 需要獲取的數(shù)據(jù) |
其中 type 和 id 兩個(gè)字段作為聯(lián)合主鍵確定一條記錄,其中部分?jǐn)?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 | =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 |
代碼5.4
代碼5.4詳解:
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:使用循環(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ú)立的組表文件:
A | B | |
1 | fork directory@p("nid*T.ctx") | =file(A1).create().index(nid_idx;nid;data) |
代碼5.5
代碼5.5,創(chuàng)建索引過(guò)程詳解:
A1:列出滿足 nid*T.ctx 的文件名(這里 * 為通配符),這里 @p 選項(xiàng)代表需要返回帶有完整路徑信息的文件名。使用 fork 執(zhí)行多線程時(shí),需要注意環(huán)境中的并行限制數(shù)是否設(shè)置合理。這里用了 4 個(gè)線程,設(shè)計(jì)器中對(duì)應(yīng)的設(shè)置如下:
B1:每個(gè)線程為各個(gè)組表建立對(duì)應(yīng)的索引文件,最終結(jié)果如下:
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) |
代碼5.6
代碼5.6,查詢過(guò)程詳解:
A1:從 keys.txt 獲取查詢鍵值序列,因?yàn)橹挥幸涣薪Y(jié)果,使用 @i 選項(xiàng),將結(jié)果返回成序列:
A2:把 A1 的序列按 4 的余數(shù)進(jìn)行等值分組:
A3、B3~B5:用 fork 函數(shù),按等值分組后的鍵值對(duì)各個(gè)組表分別并行查詢。這里的 fork 后面分別寫(xiě)了兩個(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)容如下。
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。
網(wǎng)站名稱:性能優(yōu)化技巧-查找-創(chuàng)新互聯(lián)
文章轉(zhuǎn)載:http://muchs.cn/article4/dodcoe.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站建設(shè)、響應(yīng)式網(wǎng)站、外貿(mào)建站、靜態(tài)網(wǎng)站、自適應(yīng)網(wǎng)站、做網(wǎng)站
聲明:本網(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)
猜你還喜歡下面的內(nèi)容