排序計(jì)算是一個(gè)非常消耗資源的操作,特別是對(duì)于大數(shù)據(jù)排序,如果內(nèi)存無法裝下數(shù)據(jù),常規(guī)的做法就需要借助外存,不過因此也會(huì)增加對(duì)數(shù)據(jù)的讀寫操作,而讀寫操作通常又會(huì)比排序操作更消耗資源。

本文介紹的SPL排序優(yōu)化技巧,除了提供常規(guī)的排序算法外,還根據(jù)不同場(chǎng)景下的數(shù)據(jù)特性提供了排序的替代算法,從而減少比較次數(shù)和IO量,提升運(yùn)算性能。
當(dāng)數(shù)據(jù)可以輕松裝入內(nèi)存時(shí),可以使用SPL的內(nèi)存排序函數(shù),如A.sort()。SPL默認(rèn)的排序算法是基于merge sort的多線程排序算法,也就是說,此時(shí)的優(yōu)化方式主要是通過增加線程數(shù)量實(shí)現(xiàn)的。實(shí)際采用的線程數(shù)由集算器配置中的[大并行數(shù)]指定。示例代碼如下:
| A | B | |
| 1 | =5000*1000 | /元素?cái)?shù) |
| 2 | =A1\1000 | /隨機(jī)數(shù)大值 |
| 3 | =to(A1).(rand(A2)) | /生成隨機(jī)序列 |
| 4 | =now() | /當(dāng)前時(shí)間 |
| 5 | =A3.sort() | /升序排序 |
| 6 | =interval@ms(A4,now()) | /排序花費(fèi)的時(shí)間 |
實(shí)測(cè)使用的的測(cè)試機(jī)CPU是酷睿i7 ,4核心 8線程,根據(jù) [大并行數(shù)]配置的不同,測(cè)試結(jié)果如下:
| 大并行數(shù) | 平均花費(fèi)時(shí)間(毫秒) |
| 1(即單線程) | 1800 |
| 4 | 800 |
| 8 | 660 |
可見,多核心CPU或多CPU計(jì)算機(jī)通過多線程排序可以充分利用每個(gè)核心的并行計(jì)算能力,顯著提升排序性能。
此例中每個(gè)值的重復(fù)量平均為1000,對(duì)A.sort()函數(shù)來說,重復(fù)數(shù)量的多少對(duì)性能影響不大。但在重復(fù)數(shù)量較多時(shí),我們還可以通過分組法A.group@s()進(jìn)行排序,進(jìn)一步提高性能。此方法首先利用哈希法對(duì)元素進(jìn)行分組,然后再對(duì)組進(jìn)行排序,最后合并排序后的組得到排序結(jié)果。示例代碼如下:
| A | B | |
| 1 | =5000*1000 | /元素?cái)?shù) |
| 2 | =A1\1000 | /隨機(jī)數(shù)大值 |
| 3 | =to(A1).(rand(A2)) | /生成隨機(jī)序列 |
| 4 | =now() | /當(dāng)前時(shí)間 |
| 5 | =A3.group@s() | /每個(gè)值平均有1000個(gè)重復(fù)的,使用分組法進(jìn)行升序排序 |
| 6 | =interval@ms(A4,now()) | /排序花費(fèi)的時(shí)間 |
使用分組法排序后,平均花費(fèi)時(shí)間為360毫秒,可見,此方法適合重復(fù)數(shù)量較多的數(shù)據(jù),重復(fù)數(shù)量越多性能越好。
當(dāng)數(shù)據(jù)量大到無法裝入內(nèi)存時(shí)就需要借助外存進(jìn)行排序。外存排序會(huì)分步讀入數(shù)據(jù),排序后寫出到臨時(shí)文件,最后對(duì)所有生成的臨時(shí)文件歸并得到最終結(jié)果。
臨時(shí)文件的數(shù)量會(huì)影響排序的效率,如果數(shù)量過多,歸并階段會(huì)占用更多的資源,而歸并路數(shù)過多也會(huì)影響歸并效率。所以每次讀入較多的數(shù)據(jù)量可以提升sortx的性能。
SPL外存排序的函數(shù)是cs.sortx(),排序中生成的臨時(shí)文件數(shù)量由原始數(shù)據(jù)量和每次讀入的數(shù)據(jù)量決定,而每次讀入的數(shù)據(jù)量可以通過sortx函數(shù)的參數(shù)指定。用戶可以根據(jù)記錄大小和空閑內(nèi)存容量指定一個(gè)合理的每次讀入數(shù)據(jù)量以達(dá)到最優(yōu)的性能。如果沒有指定,SPL會(huì)根據(jù)虛擬機(jī)可用內(nèi)存估算出一個(gè)大概值。
臨時(shí)文件的釋放會(huì)在結(jié)果游標(biāo)取數(shù)結(jié)束或者close方法被調(diào)用時(shí)觸發(fā)。
測(cè)試外存排序需要大數(shù)據(jù)量,而由于使用JDBC從數(shù)據(jù)庫(kù)取數(shù)的效率非常差,因此本文將采用效率更好的集文件進(jìn)行測(cè)試。
測(cè)試數(shù)據(jù)模擬訂單數(shù)據(jù),表結(jié)構(gòu)為{ order_id ,order_datetime , customer_id, employee_id , product_id , quantity ,price},按order_id 、order_datetime有序,隨機(jī)生成2000萬條記錄。
測(cè)試數(shù)據(jù)的生成也使用SPL,造數(shù)代碼如下:
| A | B | |
| 1 | 2018-01-01 00:00:00 | =file("orders_2018.btx") |
| 2 | =to(1000*1000) | 0 |
| 3 | for 20 | =A2.new(B2+~:order_id, elapse@s(A1,order_id):order_datetime, rand(1000000) + 1:customer_id, rand(1000) + 1:employee_id, rand(10000) + 1:product_id, rand(1000) + 1:quantity, rand(100000000)/100:price) |
| 4 | =B1.export@ab(B3) | |
| 5 | >B2=B2+A2.len() |
排序計(jì)算的代碼如下:
| A | B | |
| 1 | =now() | |
| 2 | =file("orders_2018.btx").cursor@b() | /生成訂單集文件的取數(shù)游標(biāo) |
| 3 | =A2.sortx(customer_id; 2000000) | /對(duì)游標(biāo)按客戶編碼排序,第二個(gè)參數(shù)為每次讀入的數(shù)據(jù)量 |
| 4 | =file("orders_customer_2018.btx").export@b(A3) | /導(dǎo)出排序結(jié)果到集文件 |
| 5 | =interval@s(A1,now()) | /耗時(shí),單位秒 |
測(cè)試結(jié)果如下:
| 每次讀入數(shù)據(jù)量 | 耗時(shí)(秒) |
| 200萬 | 73 |
| 20萬 | 216 |
使用SPL處理大數(shù)據(jù)運(yùn)算時(shí),為了獲取更好的性能通常會(huì)把數(shù)據(jù)外置成集文件或組表,同時(shí)按照常用的過濾維度排序以獲得更高的過濾性能。這樣,如果有新的數(shù)據(jù)要追加到歷史文件,并需要對(duì)所有數(shù)據(jù)重新排序,我們就可以利用集文件或者組表的這個(gè)特點(diǎn)了。由于歷史數(shù)據(jù)已經(jīng)有序,此時(shí)我們可以先把新數(shù)據(jù)按照維度排序,然后再和歷史數(shù)據(jù)進(jìn)行歸并就可以得到最終的有序數(shù)據(jù)了。使用這個(gè)方法排序涉及的數(shù)據(jù)量遠(yuǎn)小于對(duì)所有數(shù)據(jù)進(jìn)行大排序的數(shù)據(jù)量。具體的測(cè)試如下:
在前面外存排序的造數(shù)代碼中,將A1格改為2017-01-01 00:00:00,B1格改為=file("orders_2017.btx"),生成模擬2017年的訂單數(shù)據(jù)文件“orders_2017.btx”。然后使用外存排序代碼按customer_id字段排序,生成排序結(jié)果文件“orders_2017_customer.btx”。
然后,我們需要將2017、2018兩年的所有數(shù)據(jù)進(jìn)行整體排序,也就是使用歸并方法合并orders_2017_customer.btx、orders_2018_customer.btx兩個(gè)文件成一個(gè)新的有序文件。示例代碼如下:
| A | B | |
| 1 | =now() | |
| 2 | =file("orders_2017_customer.btx").cursor@b() | |
| 3 | =file("orders_2018_customer.btx").cursor@b() | |
| 4 | =[A2,A3].mergex(customer_id) | /對(duì)兩個(gè)按customer_id有序的游標(biāo)做歸并,合成一個(gè)按customer_id有序的新游標(biāo) |
| 5 | =file("orders1.btx").export@b(A4) | /把排序后的數(shù)據(jù)導(dǎo)出到集文件 |
| 6 | =now() | =interval@s(A1,A6) |
| 7 | =file("orders_2017_customer.btx").cursor@b() | |
| 8 | =file("orders_2018_customer.btx").cursor@b() | |
| 9 | =[A7,A8].conjx().sortx(customer_id; 2000000) | /縱向連接兩個(gè)游標(biāo)并進(jìn)行外存排序 |
| 10 | =file("orders2.btx").export@b(A9) | |
| 11 | =now() | =interval@s(A6,A11) |
前6行代碼采用了歸并方法,耗時(shí)30秒,而后5行代碼模擬了簡(jiǎn)單的大排序方法,耗時(shí)133秒。
如果需要按照多個(gè)字段對(duì)數(shù)據(jù)進(jìn)行排序,而數(shù)據(jù)已經(jīng)按照排序字段中的幾個(gè)字段有序了,則可以按照已經(jīng)有序的字段分組讀入數(shù)據(jù),在內(nèi)存排序后輸出。這種處理方法比cs.sortx()少了一遍讀寫操作,因此性能大幅優(yōu)于cs.sortx()。
例如訂單表的數(shù)據(jù)是按照日期時(shí)間有序生成的,如果想按照日期、客戶兩個(gè)字段對(duì)訂單表進(jìn)行排序則可以使用這個(gè)方法。示例代碼如下:
| A | B | |
| 1 | =now() | |
| 2 | =file("orders_2017.btx").cursor@b() | |
| 3 | =A2.run(order_datetime=date(order_datetime)) | /把日期時(shí)間轉(zhuǎn)為日期類型 |
| 4 | =A2.group@qs(order_datetime;customer_id) | /數(shù)據(jù)已按order_datetime有序,對(duì)數(shù)據(jù)按order_datetime,customer_id排序 |
| 5 | =file("orders_2017_date_customer.btx").export@b(A4) | |
| 6 | =interval@s(A1,now()) |
經(jīng)測(cè)試耗時(shí)為38秒(A6格的值),而如果把A4格表達(dá)式替換為下面代碼則耗時(shí)95秒。
| A | B | |
| 4 | =A2.sortx(order_datetime,customer_id;2000000) | /數(shù)據(jù)已按order_datetime有序,對(duì)數(shù)據(jù)按order_datetime,customer_id排序 |
SPL的組表提供了為某些列創(chuàng)建索引的功能,一些常用的列也可以存到索引里,這樣如果訪問的列都在索引里就不需要再訪問整個(gè)源文件,從而節(jié)省大量IO操作。而如果排序字段是索引字段,并且需要訪問的字段也都在索引里,則可以利用索引的有序性,使用T.icursor@s()返回有序游標(biāo)。
創(chuàng)建索引的代碼如下:
| A | B | |
| 1 | 2018-01-01 00:00:00 | =file("orders.ctx") |
| 2 | =to(1000*1000) | 0 |
| 3 | =B1.create@y(#order_id,#order_datetime,customer_id,employee_id,product_id,quantity, price) | |
| 4 | /以下循環(huán)為創(chuàng)建組表數(shù)據(jù)程序 | |
| 5 | for 20 | =A2.new(B2+~:order_id, elapse@s(A1,order_id):order_datetime, rand(1000000) + 1:customer_id, rand(1000) + 1:employee_id, rand(10000) + 1:product_id, rand(1000) + 1:quantity, rand(100000000)/100:price) |
| 6 | =A3.append(B5.cursor()) | |
| 7 | >B2=B2+A2.len() | |
| 8 | =A3.index(PriceIndex;price;product_id,order_datetime) | /按金額字段建索引,同時(shí)保持產(chǎn)品,日期兩個(gè)字段的值 |
利用索引產(chǎn)生有序游標(biāo)代碼如下:
| A | B | |
| 1 | =now() | =file("orders.ctx").create() |
| 2 | =B1.icursor@s(order_datetime,product_id,price;true,PriceIndex) | /利用索引返回有序游標(biāo) |
| 3 | for A2,10000 | /遍歷游標(biāo)數(shù)據(jù) |
| 4 | =now() | =interval@ms(A1,A4) |
| 5 | =B1.cursor(order_datetime,product_id,price) | |
| 6 | =A5.sortx(price) | /外存排序 |
| 7 | for A6,10000 | /遍歷游標(biāo)數(shù)據(jù) |
| 8 | =now() | =interval@ms(A4,A8) |
經(jīng)測(cè)試有序游標(biāo)耗時(shí)為4秒(B4格的值),而外存排序遍歷耗時(shí)為54秒(B8格的值)。
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+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)站標(biāo)題:SPL排序優(yōu)化技巧-創(chuàng)新互聯(lián)
網(wǎng)頁網(wǎng)址:http://chinadenli.net/article26/dgpecg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供面包屑導(dǎo)航、網(wǎng)站導(dǎo)航、自適應(yīng)網(wǎng)站、動(dòng)態(tài)網(wǎng)站、小程序開發(fā)、營(yíng)銷型網(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í)需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容