日常生活中,我們會遇到各種各樣的數(shù)據(jù),小到公司通訊錄,大到互聯(lián)網(wǎng)用戶行為分析。在進行數(shù)據(jù)分析處理的過程中,查詢是必不可少的環(huán)節(jié),如何更加高效地進行數(shù)據(jù)查詢。
創(chuàng)新互聯(lián)專注于企業(yè)營銷型網(wǎng)站建設、網(wǎng)站重做改版、烏蘇網(wǎng)站定制設計、自適應品牌網(wǎng)站建設、H5頁面制作、商城建設、集團公司官網(wǎng)建設、成都外貿(mào)網(wǎng)站建設、高端網(wǎng)站制作、響應式網(wǎng)頁設計等建站業(yè)務,價格優(yōu)惠性價比高,為烏蘇等各大城市提供網(wǎng)站開發(fā)制作服務。SPL為用戶提供了強大的索引機制以及針對不同場景中各對象的查詢函數(shù),善加運用,可以顯著提高查詢性能。
我們先建立一個份“通話記錄”的模擬數(shù)據(jù),通過這份數(shù)據(jù),來比較一下不同查詢函數(shù)對序表查詢性能的影響。建立模擬數(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
其中部分數(shù)據(jù)如下:
圖1.1.1
對序表進行查詢,通常我們會想到使用A.select()函數(shù)。我們來看一下使用該函數(shù)的效果:
A | B | |
1 | =file("btx/voiceBill.btx").import@b() | /讀取文件中的數(shù)據(jù),作為序表 |
2 | =now() | /當前時間 |
3 | =A1.select(Subscriber==13800263524) | /使用A.select()函數(shù)進行查詢 |
4 | =interval@ms(A2,now()) | /查詢耗時 |
代碼1.1.2
查詢耗時為80毫秒。
對序表的鍵值進行查詢時,可以利用A.find()函數(shù)進行查詢。示例代碼如下:
A | B | |
1 | =file("btx/voiceBill.btx").import@b() | /讀取文件中的數(shù)據(jù),作為序表 |
2 | >A1.keys(Subscriber) | /設置Subscriber為主鍵 |
3 | >A1.index() | /建立索引 |
4 | =now() | /當前時間 |
5 | =A1.find(13800263524) | /使用A.find()函數(shù)查找 |
6 | =interval@ms(A4,now()) | /查詢花費的時間 |
代碼1.1.3
查詢耗時為1毫秒。
這是因為在集算器的序表中,可以指定某個或某些字段作為主鍵,基于主鍵的查找可以使用專門的函數(shù)。比如代碼1.1.3中A5的find函數(shù),不僅能簡化書寫,更能有效地提高計算性能。
當鍵值較多時,我們使用函數(shù)A.find@k ()進行批量鍵值查找。示例代碼如下:
A | B | |
1 | =file("btx/voiceBill.btx").import@b() | /讀取文件中的數(shù)據(jù),作為序表 |
2 | >A1.keys(Subscriber) | /設置Subscriber為主鍵 |
3 | >A1.index() | /建立索引 |
4 | =A1(50.(rand(A1.len())+1)).(Subscriber) | /從Subscriber中隨機取50個值 |
5 | =now() | /當前時間 |
6 | =A1.find@k(A4) | /使用A.find@k()函數(shù)對A4進行批量鍵值查找 |
7 | =interval@ms(A5,now()) | /查詢花費的時間 |
代碼1.1.4
要注意的是,在使用A.find()函數(shù)時,需事先建立主鍵,否則會報“缺少主鍵”的錯誤。
利用主鍵值查找的函數(shù),可以有效地提升計算性能,是由于在序表中為主鍵建立索引表。在代碼1.1.4中,未建立索引時,平均查詢時間在1400毫秒左右;建立索引后,查詢平均耗時不到1毫秒。
序表中的數(shù)據(jù)量越大,需要查找的次數(shù)越多,對效率的提升就越明顯。
當查詢條件對應多個鍵時,示例代碼如下:
A | B | |
1 | =file("btx/voiceBill.btx").import@b() | /讀取文件中的數(shù)據(jù),作為序表 |
2 | >A1.keys(Subscriber,isLocal) | /設置Subscriber,isLocal為主鍵 |
3 | >A1.index() | /建立索引 |
4 | =[[13800000002,"F"],[13802568478,"F"]] | /鍵有多個,因此組成序列 |
5 | =now() | /當前時間 |
6 | =A1.find@k(A4) | /使用A.find@k()函數(shù)對A4進行批量鍵值查找 |
7 | =interval@ms(A5,now()) | /查詢花費的時間 |
代碼1.1.5
switch/join函數(shù)同樣需要根據(jù)主鍵值在序表中查找記錄,使用時會對維表自動建立索引。若在多線程fork函數(shù)之前沒有對相應維表建立索引,就會在每個線程中都自動為該維表建立一個索引,執(zhí)行過程中會消耗更多內(nèi)存,這樣有可能會造成內(nèi)存溢出,如圖1.1.1.2,要注意避免,較好的處理方式可以參考圖?1.1.3。
圖?1.1.2 fork中的每個線程都自動建立了索引導致內(nèi)存溢出
圖?1.1.3 fork執(zhí)行前,先對維表建立索引
對有序的集文件進行查找,可以使用f.iselect()函數(shù)實現(xiàn)二分查找,該函數(shù)也支持批量查找,下面是個基于集文件使用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萬個Subscriber中隨機找50個,去重并排序 |
3 | =now() | /當前時間 |
4 | =A1.iselect@b(A2,Subscriber) | /對集文件使用f.iselect()二分查找函數(shù)進行批量查找 |
5 | =A4.fetch() | |
6 | =interval@ms(A3,now()) | /查詢花費的時間 |
代碼1.2.2
代碼1.2.2,因為f.iselect()是個二分查找函數(shù),所以需要注意代碼中的A2作為查詢序列,與集文件的編號一樣,都需要有序。還要注意,這里的選項@b不是二分法的意思,而是讀取通過f.export()函數(shù)導出的集文件。該集文件導出時,注意需要使用選項@z,否則在使用f.iselect ()對集文件進行查詢時會報錯。
假設數(shù)據(jù)總量為N,使用二分法進行查找的時間復雜度為logN(以 2 為底),當數(shù)據(jù)量越大,性能提升也就越明顯。
組表也有類似序表的T.find()和T.find@k()函數(shù),可以高效地實現(xiàn)鍵值查找。適合于在大維表中找出少量記錄的場景。我們來看這樣一個例子:
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() | /打開組表 |
2 | =13801701672 | /組表數(shù)據(jù)其中的一個Subscriber值 |
3 | =now() | /當前時間 |
4 | =A1.cursor().select(Subscriber==A2).fetch() | /使用cs.select()對組表進行查詢 |
5 | =interval@ms(A3,now()) | /查詢花費的時間 |
代碼1.3.2
代碼1.3.2,對組表使用cs.select()函數(shù)進行查詢,耗時為:13855毫秒。
A | B | |
1 | =file("ctx/voiceBill.ctx").create() | /打開組表 |
2 | =13801701672 | /組表數(shù)據(jù)其中的一個Subscriber值 |
3 | =now() | /當前時間 |
4 | =A1.find(A2) | /使用T.find()對組表進行查詢 |
5 | =interval@ms(A3,now()) | /查詢花費的時間 |
代碼1.3.3
代碼1.3.3,對組表使用T.find()函數(shù)進行查詢,耗時為:77毫秒。
對比可見:對于有維的組表,可以使用類似序表的T.find()函數(shù),進行單個或者批量鍵值的查詢,其查詢效率遠高于從篩選后的游標中取數(shù)。
組表上可以建立三種索引,每種索引針對的情況也不同,分別為:
1、? hash索引,適合單值查找,比如枚舉類型;
2、? 排序索引,適合區(qū)間查找,比如數(shù)字、日期、時間類型;
3、? 全文索引,用于模糊查詢,比如字符串類型。
下面我們來建立一個組表,使其數(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() | /打開組表 |
2 | =A1.index(subscriber_idx;Subscriber) | /用戶手機號碼,數(shù)值用排序索引 |
3 | =A1.index(time_idx;Time) | /通話開始時間,數(shù)值用排序索引 |
4 | =A1.index(length_idx;Length) | /通話時長,數(shù)值用排序索引 |
5 | =A1.index(city_idx:1;City) | /城市,枚舉用hash索引 |
6 | =A1.index@w(company_idx;Company) | /公司,字串用全文索引 |
代碼2.2
代碼2.2,根據(jù)每列數(shù)據(jù)類型的特點,建立不同類型的索引。建立好的索引和組表文件如圖2.2:
圖2.2
集算器能自動識別條件找到合適的索引,等值和區(qū)間都可以,like(“A*”)式的也支持。我們來看下效果:
A | B | |
1 | =file("ctx/voiceBillDetail.ctx").create() | /打開組表 |
2 | =now() | /當前時間 |
3 | =A1.icursor(;Subscriber==13834750766,subscriber_idx).fetch() | /icursor查詢 |
4 | =interval@ms(A2,now()) | /查詢耗時 |
代碼2.3
A | B | |
1 | =file("ctx/voiceBillDetail.ctx").create() | /打開組表 |
2 | =now() | /當前時間 |
3 | =A1.icursor(;Subscriber==13834750766).fetch() | /icursor查詢 |
4 | =interval@ms(A2,now()) | /查詢耗時 |
代碼2.4
代碼2.3是沒有省略索引名稱的寫法,代碼2.4是省略索引名稱的寫法。兩者時間消耗基本相同,都是100毫秒左右。
A | B | |
1 | =file("ctx/voiceBillDetail.ctx").create() | /打開組表 |
2 | =now() | /當前時間 |
3 | =A1.cursor().select(Subscriber==13834750766).fetch() | /普通游標查詢 |
4 | =interval@ms(A2,now()) | /查詢耗時 |
代碼2.5
代碼2.5使用普通游標查詢同樣的記錄,查詢耗時則需要40秒左右。
A | B | |
1 | =file("ctx/voiceBillDetail.ctx").create() | /打開組表 |
2 | =now() | /當前時間 |
3 | =A1.icursor(;Subscriber>=13834750766 && ? Subscriber<=13834750780).fetch() | /icursor查詢 |
4 | =interval@ms(A2,now()) | /查詢耗時 |
代碼2.6
代碼2.6對Subscriber使用排序索引,進行區(qū)間查找,查詢耗時是70毫秒左右。
A | B | |
1 | =file("ctx/voiceBillDetail.ctx").create() | /打開組表 |
2 | =now() | /當前時間 |
3 | =A1.cursor().select(Subscriber>=13834750766 ? && Subscriber<=13834750780).fetch() | /普通游標查詢 |
4 | =interval@ms(A2,now()) | /查詢耗時 |
代碼2.7
代碼2.7使用普通游標查詢同樣條件的記錄,查詢耗時則需要40秒左右。
A | B | |
1 | =file("ctx/voiceBillDetail.ctx").create() | /打開組表 |
2 | =now() | /當前時間 |
3 | =A1.icursor(like(Company,"*ABCDE*")).fetch() | /icursor查詢 |
4 | =interval@ms(A2,now()) | /查詢耗時 |
代碼2.8
代碼2.8對Company使用全文索引,進行模糊查詢,查詢耗時是1500毫秒左右。
A | B | |
1 | =file("ctx/voiceBillDetail.ctx").create() | /打開組表 |
2 | =now() | /當前時間 |
3 | =A1.cursor().select(like(Company,"*ABCDE*")).fetch() | /普通游標查詢 |
4 | =interval@ms(A2,now()) | /查詢耗時 |
代碼2.9
代碼2.9使用普通游標查詢同樣條件的記錄,查詢耗時則需要40秒左右。
當數(shù)據(jù)規(guī)模更大時,例如:
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() | /打開組表 |
2 | =A1.index(id_idx;id) | /編號,數(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中,對大部分列建立了索引。組表與索引的各個文件如圖2.4。
圖2.4
多等值條件項&&時,可以分別為每個字段建立索引。集算器能夠快速在多個索引中用歸并算法計算交集。比如:
A | B | |
1 | =file("ctx/employee.ctx").create() | /打開組表 |
2 | =now() | /當前時間 |
3 | =A1.icursor(;city=="Casper" ? && salary==25716).fetch() | /icursor查詢多個等值條件 |
4 | =interval@ms(A2,now()) | /查詢耗時 |
代碼2.12
代碼2.12,查詢條件均為等值查詢,A3查出記錄數(shù)為324條,耗時31883毫秒。
但區(qū)間條件時不能再用歸并計算交集,集算器將只對其中一個條件使用索引,另一個條件使用遍歷計算,效果就會差,比如:
A | B | |
1 | =file("ctx/employee.ctx").create() | /打開組表 |
2 | =now() | /當前時間 |
3 | =A1.icursor(;height>208 && ? weight<70? && ? salary==21765).fetch() | /icursor查詢多個區(qū)間條件 |
4 | =interval@ms(A2,now()) | /查詢耗時 |
代碼2.13
代碼2.13,查詢條件均為區(qū)間條件,A3查出記錄數(shù)為389條,耗時70283毫秒。
組表索引提供了兩級緩存機制,可以用index@2或者index@3預先把索引的索引讀入內(nèi)存,如果需要重復多次使用索引查找,則可以有效提高性能。
選項@2、@3的意思分別是將索引的第二、三級緩存先加載進內(nèi)存。經(jīng)過索引緩存的預處理,第一遍查詢時間也能達到查詢數(shù)百次后才能達到的極限值。@2相比@3緩存的內(nèi)容少,效果相對差一點,但內(nèi)存占用也更少。使用時需要程序員根據(jù)具體場景來權(quán)衡@2還是@3。
A | B | |
1 | =file("ctx/employee.ctx").create() | /打開組表 |
2 | =now() | /當前時間 |
3 | =A1.icursor(;city=="Casper" ? && salary==25716).fetch() | /icursor查詢 |
4 | =interval@ms(A2,now()) | /查詢耗時 |
代碼3.1
代碼3.1,基于代碼2.10建造的組表文件,不使用索引緩存,查詢耗時為31883毫秒。
A | B | |
1 | =file("ctx/employee.ctx").create() | /打開組表 |
2 | =A1.index@3(city_idx) | /使用第三級緩存 |
3 | =A1.index@3(salary_idx) | /使用第三級緩存 |
4 | =now() | /當前時間 |
5 | =A1.icursor(;city=="Casper" ? && salary==25716).fetch() | /icursor查詢 |
6 | =interval@ms(A4,now()) | /查詢耗時 |
代碼3.2
代碼3.2使用第三級索引緩存,查詢耗時為5225毫秒。
這里使用的是列存組表,列存采用了數(shù)據(jù)分塊并壓縮的算法,對于遍歷運算來講,訪問數(shù)據(jù)量會變小,也就會具有更好的性能。但對于基于索引隨機取數(shù)的場景,由于要有額外的解壓過程,而且每次取數(shù)都會針對整個分塊,運算復雜度會高很多。因此,從原理上分析,這時候的性能應當會比行存要差。將組表轉(zhuǎn)為行存后,查詢耗時僅為1592毫秒。
索引緩存在并行時可以復用,如下:
A | B | |
1 | =file("ctx/employee.ctx").create() | /打開組表 |
2 | =A1.index@3(city_idx) | /使用第三級緩存 |
3 | =A1.index@3(salary_idx) | /使用第三級緩存 |
4 | =now() | /當前時間 |
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()) | /并行查詢耗時 |
代碼3.3
代碼3.3,并行時,A5的每個線程中都可以使用A2、A3中建立的第三級索引緩存,最終查詢耗時為21376毫秒。
組表的行存和列存形式都支持索引,列存索引查找比行存性能差,返回結(jié)果集較少時差異不明顯,大量返回時會有明顯劣勢,在設計存儲方案時要權(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 個英文字母和 10 個阿拉伯數(shù)字的字符串。
A2、A3:建立結(jié)構(gòu)為 (id,data) 的組表文件,使用列式存儲方式。
A4:循環(huán) 6000 次,循環(huán)體B4、B5,每次生成 10 萬條對應結(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。
列存組表生成時 create() 函數(shù)加上 @r 選項,即可變?yōu)樯尚写娼M表,其余代碼無異,這里不再舉例,當返回數(shù)據(jù)量較大時:
A | B | |
1 | =10000.(rand(600000000)+1000000000001).id() | /根據(jù)數(shù)據(jù)特點,從六億中隨機取一萬個待查詢的測試id鍵值并去重 |
2 | =file("id_600m.ctx").create() | /打開列存組表 |
3 | =now() | /當前時間 |
4 | =A2.icursor(;A1.contain(id),id_idx).fetch() | /icursor查詢A1中對應的id鍵值 |
5 | =interval@ms(A3,now()) | /列存查詢耗時 |
6 | =file("id_600m@r.ctx").create() | /打開行存組表 |
7 | =now() | /當前時間 |
8 | =A6.icursor(;A1.contain(id),id_idx).fetch() | /icursor查詢A1中對應的id鍵值 |
9 | =interval@ms(A7,now()) | /行存查詢耗時 |
代碼4.3
代碼4.3中,列存查詢耗時和行存查詢耗時,也就是A5和A9的值分別為205270和82800毫秒。
組表支持一種帶值索引,即把查找字段也寫入索引,這樣可以不再訪問原組表即返回結(jié)果。但存儲空間會占用較多。
基于代碼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列建立索引,在對組表建立索引時,當 index 函數(shù)有數(shù)據(jù)列名參數(shù),如本例 A2 中的 data,就會在建索引時把數(shù)據(jù)列 data 復制進索引。當有多個數(shù)據(jù)列時,可以寫為:index(id_idx;id;data1,data2,…)。
因為在索引中做了冗余,索引文件也自然會較大,本文中測試的列存組表和索引冗余后的文件大小為:
類型 | 文件名 | 大小 |
組表 | id_600m.ctx | 105G |
索引 | id_600m.ctx__id_data_idx | 112G |
當數(shù)據(jù)復制進索引后,實際上讀取時不再訪問原數(shù)據(jù)文件了。
從 6 億條數(shù)據(jù)總量中取 1 萬條批量隨機鍵值,完整的測試結(jié)果對比:
耗時(毫秒) | |||||
單線程 | 多線程(10 線程) | ||||
Oracle | 行存索引 | 冗余索引 | Oracle | 行存索引 | 冗余索引 |
117322 | 20745 | 19873 | 39549 | 10975 | 9561 |
組表索引能夠識別出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 個英文字母和 10 個阿拉伯數(shù)字的字符串。
A2、A3:建立結(jié)構(gòu)為 (id,data) 的組表文件,@r 選項表示使用行式存儲方式。
A4:循環(huán) 6000 次,循環(huán)體B4、B5,每次生成 10 萬條對應結(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() | /打開組表 |
2 | =now() | /當前時間 |
3 | =A1.index@3(id_idx) | /使用index@3加載索引緩存 |
4 | =interval@ms(A2,now()) | /加載索引緩存的時間 |
5 | =10000.(1000000000000+(rand(600000000)+1)).sort() | /隨機取一些鍵值并排序 |
6 | =now() | /當前時間 |
7 | =A1.icursor(A5.contain(id),id_idx).fetch() | /使用icursor函數(shù)進行批量鍵值查詢 |
8 | =interval@ms(A6,now()) | /查詢耗時 |
代碼5.3
代碼5.3,在組表的 icursor()這個函數(shù)中,使用索引 id_idx,以條件 A2.contain(id) 來過濾組表。集算器會自動識別出 A2.contain(id) 這個條件可以使用索引,并會自動將 A2 的內(nèi)容排序后從前向后查找。
進階使用
使用排序索引多線程查找時,按鍵值排序分組后扔給多個線程去查詢,避免兩個線程中有交叉內(nèi)容。同時,還可以設計成多個組表,把鍵值能平均分配到多個組表上并行查找。
所謂多線程并行,就是把數(shù)據(jù)分成 N 份,用 N 個線程查詢。但如果只是隨意地將數(shù)據(jù)分成 N 份,很可能無法真正地提高性能。因為將要查詢的鍵值集是未知的,所以理論上也無法確保希望查找的數(shù)據(jù)能夠均勻分布在每一份組表文件中。比較好的處理方式是先觀察鍵值集的特征,從而盡可能地進行數(shù)據(jù)的均勻拆分。
如果鍵值數(shù)據(jù)有比較明顯的業(yè)務特征,我們可以考慮按照實際業(yè)務場景使用日期、部門之類的字段來處理文件拆分。如:將屬于部門 A 的 1000 條記錄均分在 10 個文件中,每個文件就有 100 條記錄。在利用多線程查詢屬于部門 A 的記錄時,每個線程就會從各自對應的文件中取數(shù)相應的這 100 條記錄了。
下面我們來看個實際的例子,已有數(shù)據(jù)文件multi_source.txt的結(jié)構(gòu)如下:
字段名稱 | 類型 | 說明 |
type | string | 可枚舉 |
Id | long | 每個枚舉類型的 id 都從 1 開始自增 |
data | string | 需要獲取的數(shù)據(jù) |
其中 type 和 id 兩個字段作為聯(liá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ù)據(jù)庫數(shù)據(jù)源。。
A2:給枚舉值序列中每個 type 一個 tid。為后續(xù)的數(shù)字化主鍵合并做準備。
A3~A6:從 multi_source.txt 文件中獲取數(shù)據(jù),并按照 A2 中的對應關(guān)系,把 type 列的枚舉串變成數(shù)字,然后將 type 和 id 進行合并后,生成新的主鍵 nid。
A7:使用循環(huán)函數(shù),創(chuàng)建名為“鍵值名 _ 鍵值取 N 的余數(shù) _T.ctx”的組表文件,其結(jié)構(gòu)同為 (#nid,data)。
A8:用循環(huán)函數(shù)將游標數(shù)據(jù)分別追加到 N 個原組表上。比如當 N=1 時,拼出的 eval 函數(shù)參數(shù)為:channel(A4).select(nid%4==0).attach(A7(1).append(~.cursor()))。意思是對游標 A4 創(chuàng)建管道,將管道中記錄按鍵值 nid 取 4 的余數(shù),將余數(shù)值等于 0 的記錄過濾出來。attach 是對當前管道的附加運算,表示取和當前余數(shù)值對應的原組表,將當前管道中篩選過濾出的記錄,以游標記錄的方式追加到 A7(1),即第 1 個組表。
A9:循環(huán)游標 A6,每次獲取 50 萬條記錄,直至 A6 游標中的數(shù)據(jù)取完。
執(zhí)行后,產(chǎn)出 4(這時例子取 N=4)個獨立的組表文件:
A | B | |
1 | fork directory@p("nid*T.ctx") | =file(A1).create().index(nid_idx;nid;data) |
代碼5.5
代碼5.5,創(chuàng)建索引過程詳解:
A1:列出滿足 nid*T.ctx 的文件名(這里 * 為通配符),這里 @p 選項代表需要返回帶有完整路徑信息的文件名。使用 fork 執(zhí)行多線程時,需要注意環(huán)境中的并行限制數(shù)是否設置合理。這里用了 4 個線程,設計器中對應的設置如下:
B1:每個線程為各個組表建立對應的索引文件,最終結(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,查詢過程詳解:
A1:從 keys.txt 獲取查詢鍵值序列,因為只有一列結(jié)果,使用 @i 選項,將結(jié)果返回成序列:
A2:把 A1 的序列按 4 的余數(shù)進行等值分組:
A3、B3~B5:用 fork 函數(shù),按等值分組后的鍵值對各個組表分別并行查詢。這里的 fork 后面分別寫了兩個參數(shù),第一個是循環(huán)函數(shù) N.(~-1),第二個是 A2。在接下來的 B3、B4 中分別使用 A3(2) 和 A3(1) 來獲取 fork 后面這兩個對應順序的參數(shù),B4:對組表文件進行根據(jù) B3 中的鍵值集進行數(shù)據(jù)篩選,B5:返回游標。由于 A3 中是多個線程返回的游標序列,所以 A6 中需要使用 conjx 對多個游標進行縱向連接。
A6~A7:將多個線程返回的游標進行縱向連接后,導出游標記錄至文本文件,前幾行內(nèi)容如下。
另外有需要云服務器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務器、裸金屬服務器、高防服務器、香港服務器、美國服務器、虛擬主機、免備案服務器”等云主機租用服務以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應用場景需求。
網(wǎng)站名稱:性能優(yōu)化技巧-查找-創(chuàng)新互聯(lián)
文章轉(zhuǎn)載:http://m.newbst.com/article4/dodcoe.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站建設、響應式網(wǎng)站、外貿(mào)建站、靜態(tài)網(wǎng)站、自適應網(wǎng)站、做網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)