劉 健
(1.四川開放大學(xué)高職學(xué)院,四川 成都 610107;2.四川華新現(xiàn)代職業(yè)學(xué)院信息中心,四川 成都 610107)
現(xiàn)已有多種存儲(chǔ)數(shù)據(jù)的常規(guī)算法,相關(guān)算法也在實(shí)踐應(yīng)用中得到了證明和推廣。該文討論的算法涉及2個(gè)隊(duì)列:對(duì)象隊(duì)列和容器隊(duì)列,顯然不能簡(jiǎn)單套用已有的普適性算法,必須在該基礎(chǔ)上進(jìn)行改進(jìn)。
現(xiàn)實(shí)生活中的實(shí)際需求不會(huì)一成不變,更不可能恰好匹配常規(guī)算法的要求。實(shí)際情況常需要綜合運(yùn)用2個(gè)或多個(gè)常規(guī)算法,還要摻雜一些特殊要求。在現(xiàn)實(shí)的生產(chǎn)生活中,工作人員還可以借助通用型的工業(yè)軟件,通過(guò)半手工操作的方法來(lái)進(jìn)行常規(guī)算法形式的運(yùn)算,這種方法的效率極低。當(dāng)遇到某些特殊需求時(shí),這種半手工的方法就完全無(wú)法滿足要求。該文采用移動(dòng)窗口的方式來(lái)消除若干個(gè)對(duì)象間的社會(huì)聯(lián)系,這是半手工方法根本不能勝任的任務(wù)。
該文討論的問(wèn)題來(lái)源于一個(gè)經(jīng)常出現(xiàn)的現(xiàn)象:人或物在一個(gè)特定的環(huán)境里按照一定的規(guī)則安排存放的位置或場(chǎng)地。這些人或物可以抽象為對(duì)象,若干個(gè)人或物就組成對(duì)象隊(duì)列。當(dāng)然,這些人或物之間是要分類的,分類后就構(gòu)成異類的對(duì)象隊(duì)列。當(dāng)這些對(duì)象剛出現(xiàn)時(shí),都是無(wú)序的。至于這些對(duì)象要存放的位置或場(chǎng)地就可以抽象為容器。因?yàn)槿嘶蛭锟赡軙?huì)經(jīng)常更換,所以不能對(duì)這些對(duì)象有過(guò)多要求。而容器相對(duì)來(lái)說(shuō)不會(huì)頻繁更換,可以按照特定的標(biāo)準(zhǔn)統(tǒng)一生產(chǎn)或配置。
所有容器都是標(biāo)準(zhǔn)化的容器,記為container(=1,2,…,),其容量記為capacity(=1,2,…,)。每個(gè)容器內(nèi)部都劃分為若干個(gè)小格,稱為存儲(chǔ)格,記為cell(=1,2,…,;=1,2,…,capacity)。容器中存儲(chǔ)格的數(shù)量就是這個(gè)容器的容量。
每個(gè)容器內(nèi)部的存儲(chǔ)格之間沒(méi)有區(qū)別,即尺寸、容量、和功能都一樣,而相同類型的容器與容器間在功能上也沒(méi)有區(qū)別,都可以存儲(chǔ)同類對(duì)象,這就是標(biāo)準(zhǔn)容器。標(biāo)準(zhǔn)容器利于存儲(chǔ)個(gè)體間有細(xì)小差異的同類對(duì)象。
對(duì)象有其自身的類型,根據(jù)對(duì)象類型的不同,對(duì)存儲(chǔ)格的要求也不同,于是就需要對(duì)存儲(chǔ)格進(jìn)行分類。因?yàn)槊總€(gè)容器內(nèi)的存儲(chǔ)格性質(zhì)都相同,所以存儲(chǔ)格的類型就決定容器的類型。存儲(chǔ)格的類型與容器的類型一致,并與存儲(chǔ)對(duì)象的類型一致,如公式(1)所示。
式中:object_type(=1,2,…,)為對(duì)象的類型;cell_type(=1,2,…,)為存儲(chǔ)格類型;container_type(=1,2,…,)為容器的類型。
既然3個(gè)實(shí)體的類型都必須一致,那可以把3個(gè)類型都簡(jiǎn)化為1個(gè)類型,如公式(2)所示。
式中:type(=1,2,…,)為3個(gè)類型統(tǒng)一。
當(dāng)對(duì)象異類時(shí),就需要異類容器去匹配相應(yīng)類型的對(duì)象,以滿足相應(yīng)的存儲(chǔ)要求。對(duì)象類型的數(shù)量決定容器類型的數(shù)量,這就解決了標(biāo)準(zhǔn)容器存儲(chǔ)異類對(duì)象的問(wèn)題。
因?yàn)槭菢?biāo)準(zhǔn)容器,內(nèi)部的存儲(chǔ)格必須一致,所以可以將相同類型容器的內(nèi)部存儲(chǔ)格數(shù)量設(shè)定為相同,即同類容器的容量都一樣,這樣便于對(duì)容器進(jìn)行生產(chǎn)和管理。這種情況是一種理想狀態(tài),實(shí)踐的過(guò)程中很難滿足這種極端條件,如公式(3)所示。
式中:capacity為容器的容量,=1,2,…,。
由于容量都一致,因此可以簡(jiǎn)單記為。
在實(shí)際運(yùn)用中,因?yàn)楝F(xiàn)場(chǎng)地理?xiàng)l件等各種因素的影響,不一定只允許相同容量的容器存在,所以需要為每個(gè)容器設(shè)定自身的容量,此時(shí)各個(gè)capacity的值不盡相同。但每個(gè)容器的容量都不能超過(guò)標(biāo)準(zhǔn)容器的容量,即滿足公式(4)。
式中:為標(biāo)準(zhǔn)容器的容量;capacity為各個(gè)容器的容量,=1,2,…,。
雖然同類容器的功能一樣,但是實(shí)際每個(gè)容器的內(nèi)部構(gòu)成并不一定完全一致,只要滿足容器分類的標(biāo)準(zhǔn)即可。在使用的過(guò)程中,就需要對(duì)容器的使用順序進(jìn)行排序。對(duì)每個(gè)容器指定一個(gè)優(yōu)先級(jí),記為rank(=1,2,…,),優(yōu)先級(jí)別最高的最先使用,以rank為最高優(yōu)先級(jí)。當(dāng)然也可以優(yōu)先級(jí)別最低的最先使用,這只需要倒序排序即可。在實(shí)際操作中,正序和倒序?qū)Y(jié)果沒(méi)有影響,簡(jiǎn)單說(shuō)就是排在最前的容器最先使用。
因?yàn)榇嬖诜菢?biāo)準(zhǔn)容器,所以為保證標(biāo)準(zhǔn)容器優(yōu)先使用,就需要保證大容量容器的優(yōu)先級(jí)高于小容量容器,即滿足公式(5)。
式中:capacity(=1,2,…,)為排序后各個(gè)容器的容量。
對(duì)象以批量的形式存在,即對(duì)象是整體出現(xiàn)或整體消亡的無(wú)序隊(duì)列,不是單個(gè)出現(xiàn)、逐個(gè)到達(dá)后形成隊(duì)列的。整個(gè)無(wú)序隊(duì)列存儲(chǔ)進(jìn)標(biāo)準(zhǔn)容器后,再經(jīng)過(guò)后續(xù)的相關(guān)階段,容器整體清空,待下個(gè)無(wú)序隊(duì)列存儲(chǔ)及使用。對(duì)象有批次的區(qū)分,各個(gè)批次之間有時(shí)間順序,不能同時(shí)存在。
對(duì)象隊(duì)列里存在各種類型的節(jié)點(diǎn),因?yàn)槭菬o(wú)序隊(duì)列,所以隊(duì)列里相鄰節(jié)點(diǎn)的類型不一定相同。當(dāng)相鄰節(jié)點(diǎn)類型一致時(shí),有可能出現(xiàn)若干個(gè)相鄰節(jié)點(diǎn)間的非技術(shù)性社會(huì)聯(lián)系。為消除這些社會(huì)聯(lián)系,就采用移動(dòng)窗口式的手段使這些節(jié)點(diǎn)交錯(cuò)分離,分別存儲(chǔ)在當(dāng)前窗口內(nèi)的若干個(gè)容器里,而不是連續(xù)存儲(chǔ)在同一個(gè)容器里。
在開始存儲(chǔ)對(duì)象前,必須鎖定現(xiàn)場(chǎng)所有容器,因?yàn)榇藭r(shí)尚不知道當(dāng)前對(duì)象的類型,所以是鎖定所有容器。鎖定后,不允許其他指令與該指令并行運(yùn)行,即使是讀取存儲(chǔ)容器狀態(tài)也不行(后續(xù)的操作都具有嚴(yán)格的排他性)。
如果有2個(gè)指令集并行運(yùn)行,就會(huì)出現(xiàn)臟數(shù)據(jù),后續(xù)的操作結(jié)果都是臟數(shù)據(jù)。存儲(chǔ)對(duì)象的操作只能是串行運(yùn)行的,即當(dāng)某個(gè)操作指令集到達(dá)時(shí),發(fā)現(xiàn)全部容器已經(jīng)被排他性鎖定,只能等待前一個(gè)指令集操作完畢、釋放容器后,才能對(duì)容器進(jìn)行鎖定操作。
在存儲(chǔ)列表中搜尋當(dāng)前指令的對(duì)象是否已經(jīng)存在存儲(chǔ)位置cell。如果有,就直接跳過(guò)后面的存儲(chǔ)流程,此時(shí)≠null并且≠null(1,1≤y≤capacity,null表示空)。如果沒(méi)有,就進(jìn)入下面的存儲(chǔ)流程,當(dāng)=null并且=null時(shí),進(jìn)入步驟3.3。
根據(jù)對(duì)象的類型選擇適合的容器類型,即根據(jù)type選擇類型一致的容器,拋棄不符合需求的容器。后續(xù)的操作都不再涉及容器類型的問(wèn)題,因?yàn)榇颂幰呀?jīng)作過(guò)類型篩選,只保留滿足需求的容器,所以默認(rèn)的操作都是在同種類型的容器上完成的。
首先,設(shè)定1個(gè)窗口值,采用偽隨機(jī)的方式把連續(xù)的個(gè)對(duì)象平均分配到個(gè)容器里去,目的是切斷這些對(duì)象間的社會(huì)聯(lián)系。偽隨機(jī)的方式有很多種,為計(jì)算簡(jiǎn)單、運(yùn)行高效,目前采用的是取模方式,即根據(jù)對(duì)象的下標(biāo)取模,把個(gè)對(duì)象分別依次存儲(chǔ)到個(gè)容器里,下一輪的個(gè)對(duì)象也一樣,后面的對(duì)象存儲(chǔ)方式以此類推,直到窗口所在位置的個(gè)容器全部填滿。此時(shí),窗口向后移動(dòng),躍過(guò)當(dāng)前的個(gè)容器,去覆蓋后面的個(gè)空閑容器。上一個(gè)窗口所在位置的個(gè)容器已經(jīng)填滿,就處于關(guān)閉狀態(tài)。此時(shí),窗口所在位置的容器還沒(méi)有存入對(duì)象,處于就緒狀態(tài),等待下一個(gè)對(duì)象的到來(lái)。
這樣,看上去就像窗口在不停地移動(dòng),因此稱為移動(dòng)窗口式存儲(chǔ)。理論上來(lái)說(shuō),的取值范圍為1 ~。當(dāng)= 1時(shí),全部容器根據(jù)優(yōu)先級(jí)依次使用,不存在偽隨機(jī)的存儲(chǔ)方式,也就不能發(fā)揮消除對(duì)象間社會(huì)聯(lián)系的作用。當(dāng)=時(shí),把全部的容器同時(shí)放在最大的一個(gè)窗口里,全部的容器同時(shí)處于就緒狀態(tài),整個(gè)對(duì)象隊(duì)列會(huì)平均分配到全部的容器里。顯然,在實(shí)際操作中的取值不應(yīng)該是兩頭的極限,即≠1并且≠。
此時(shí),判斷存儲(chǔ)列表是否為空。如果為空,就進(jìn)入步驟3.4.1;如果不為空,就進(jìn)入步驟3.4.2。
當(dāng)存儲(chǔ)列表為空時(shí),就表明還沒(méi)有任何一個(gè)對(duì)象已經(jīng)存儲(chǔ)到此類容器中,此類容器全部都是空閑狀態(tài)。因?yàn)槿萜饕呀?jīng)事先根據(jù)優(yōu)先級(jí)排序,所以根據(jù)這類容器的優(yōu)先級(jí)別最高級(jí)rank把當(dāng)前對(duì)象直接放在存儲(chǔ)列表的首位,即第1個(gè)容器container的第1個(gè)存儲(chǔ)格cell。因?yàn)槭谴鎯?chǔ)列表的第1條信息,所以要單獨(dú)處理。
獲取存儲(chǔ)列表中存儲(chǔ)格cell的最大值,即和的最大值。因?yàn)榇鎯?chǔ)順序是根據(jù)容器的優(yōu)先級(jí)rank和容器內(nèi)存儲(chǔ)格cell的排列次序執(zhí)行的,所以cell就是存儲(chǔ)列表末尾的那個(gè)值。
對(duì)進(jìn)行的取模運(yùn)算,即可獲得當(dāng)前窗口的位置。當(dāng)前窗口的第1個(gè)容器為container,如公式(6)所示。
式中:為當(dāng)前窗口第1個(gè)容器的編號(hào);為窗口的尺寸;為存儲(chǔ)列表末尾容器的編號(hào)。
當(dāng)前窗口的最后1個(gè)容器為container,如公式(7)所示。式中:為當(dāng)前窗口的最后1個(gè)容器的編號(hào);為窗口的尺寸;為當(dāng)前窗口第1個(gè)容器的編號(hào)。
不能簡(jiǎn)單計(jì)為(+-1),當(dāng)窗口移動(dòng)到最后1個(gè)位置時(shí),窗口內(nèi)的容器數(shù)量有可能小于窗口的值,而且出現(xiàn)這種情況的概率非常高。在實(shí)際操作中,容器隊(duì)列的數(shù)量基本是固定的,而窗口是可變值。讓容器數(shù)量剛好為每個(gè)窗口的整數(shù)倍,這幾乎是不可能的事。
在確定和的值后,就可以確定當(dāng)前窗口所在的位置。而container就是當(dāng)前窗口里的當(dāng)前容器,cell就是當(dāng)前容器里的當(dāng)前存儲(chǔ)格,當(dāng)前對(duì)象就應(yīng)該存儲(chǔ)在當(dāng)前存儲(chǔ)格cell的“下一個(gè)位置”。這個(gè)“下一個(gè)位置”有可能是下一個(gè)容器的相同位置的存儲(chǔ)格cell,也有可能是當(dāng)前窗口內(nèi)第一個(gè)容器的下一個(gè)存儲(chǔ)格cell。
此時(shí),對(duì)當(dāng)前窗口內(nèi)的所有容器進(jìn)行遍歷查找,目的是尋找合適的存儲(chǔ)位置。以當(dāng)前容器container為起始位置,獲得下一個(gè)容器container。如果container還有空余存儲(chǔ)格,即當(dāng)capacity>時(shí),那么當(dāng)前對(duì)象就存儲(chǔ)在容器container的存儲(chǔ)格cell里。如果container已經(jīng)填滿,即當(dāng)capacity=時(shí),就獲取再下一個(gè)容器container。以此類推,直到查找到當(dāng)前窗口里的最后一個(gè)容器container為止。
在完成此輪遍歷查找后,如果沒(méi)有尋找到合適的存儲(chǔ)位置,就表明當(dāng)前窗口內(nèi)當(dāng)前容器后面的容器都已經(jīng)填滿,能據(jù)此判斷這樣結(jié)果的原因就在于前置條件2.4的容量?jī)?yōu)先級(jí)。既然這些容器已滿,那只能回到當(dāng)前窗口的第1個(gè)容器container查詢空余位置。
如果container還有空余位置,即當(dāng)capacity>時(shí),當(dāng)前對(duì)象就存儲(chǔ)在容器container的存儲(chǔ)格cell 里。如果container也已經(jīng)填滿,就表明當(dāng)前窗口已滿,即整個(gè)窗口內(nèi)的全部容器都已經(jīng)填滿,只能移動(dòng)到下一個(gè)窗口。當(dāng)前對(duì)象就存儲(chǔ)在容器container的存儲(chǔ)格cell里,可以判斷當(dāng)前窗口已滿的原因也是前置條件2.4的容量?jī)?yōu)先級(jí)。
當(dāng)存儲(chǔ)操作達(dá)到閾值時(shí),就要對(duì)窗口值進(jìn)行收斂。這個(gè)閾值就是當(dāng)窗口移動(dòng)到最后一個(gè)位置,即在最后一個(gè)窗口內(nèi)操作時(shí),即為達(dá)到閾值??梢杂脤?duì)象剩余量與當(dāng)前窗口的容量比較值作為標(biāo)準(zhǔn),以判斷是否已經(jīng)到達(dá)最后一個(gè)窗口,如公式(8)所示。
式中:為對(duì)象隊(duì)列的數(shù)量;capacity為第個(gè)容器的容量。
這時(shí),就已經(jīng)到達(dá)最后一個(gè)窗口。
由公式(8)可以推導(dǎo)出公式(9)、公式(10)。
由公式(10)可知,判斷標(biāo)準(zhǔn)更簡(jiǎn)潔,從代碼的實(shí)現(xiàn)程度來(lái)說(shuō),難度也降低了。并且公式(10)的實(shí)際意義可以解釋為對(duì)象的數(shù)量小于窗口所覆蓋過(guò)的全部容器的存儲(chǔ)量的總和,包括最后一個(gè)窗口。窗口所覆蓋的全部容器不一定是整個(gè)容器隊(duì)列,其數(shù)量有可能比整個(gè)容器隊(duì)列的數(shù)量少。
此時(shí),不再使用窗口的預(yù)設(shè)值,而是把窗口收縮到最小值,即設(shè)置= 1。收斂窗口的目的是在最后一個(gè)窗口內(nèi)不作偽隨機(jī)的平鋪式存儲(chǔ),而是根據(jù)容器的優(yōu)先級(jí)依次存儲(chǔ)。
如果繼續(xù)采用偽隨機(jī)的平鋪式存儲(chǔ),那么必然會(huì)浪費(fèi)最后一個(gè)窗口內(nèi)容器的存儲(chǔ)空間(最后一個(gè)窗口內(nèi)的全部容器都會(huì)處于半空狀態(tài),即每個(gè)容器都存儲(chǔ)部分對(duì)象,同時(shí)還有空余存儲(chǔ)格)。
因?yàn)殚_啟容器就意味使用鏈條上的資源都必然配合開啟,所以半空狀態(tài)的容器必然也會(huì)浪費(fèi)鏈條上、下游的資源。
在設(shè)置= 1后,則繼續(xù)步驟3.4.2,直至對(duì)象存儲(chǔ)完畢。整個(gè)流程如圖1所示。
圖1 存儲(chǔ)流程
該文承載項(xiàng)目的單位每年舉行參與人數(shù)眾多的招錄考試,高峰時(shí)人數(shù)達(dá)到3 413人。該實(shí)例就以該數(shù)量為試驗(yàn)數(shù)據(jù)進(jìn)行演算。總體數(shù)據(jù)都處于無(wú)序狀態(tài),形成1個(gè)沒(méi)有排序標(biāo)準(zhǔn)的無(wú)序隊(duì)列。每個(gè)個(gè)體都可以抽象為1個(gè)對(duì)象,全部的對(duì)象就形成無(wú)序?qū)ο箨?duì)列。其中,有效數(shù)據(jù)3 300個(gè),無(wú)效數(shù)據(jù)113個(gè)。有效數(shù)據(jù)分2類,第Ⅰ類為3 179個(gè),第Ⅱ類為121個(gè)。為對(duì)象安排位置就可以抽象為存儲(chǔ)進(jìn)容器里的存儲(chǔ)格,該試驗(yàn)容器準(zhǔn)備情況為第Ⅰ類標(biāo)準(zhǔn)容器105個(gè),容量都為30;第Ⅰ類非標(biāo)準(zhǔn)容器3個(gè),容量分別為20、15和15;第Ⅱ類標(biāo)準(zhǔn)容器6個(gè),容量都為30。
該實(shí)例采用SQL語(yǔ)言實(shí)現(xiàn)算法,形成存儲(chǔ)過(guò)程,再使用查詢語(yǔ)句逐個(gè)對(duì)象調(diào)用存儲(chǔ)過(guò)程,模擬工作人員逐個(gè)安排存儲(chǔ)位置。試驗(yàn)環(huán)境為Interl Xeon CPU E5-2609 v2 @ 2.5GHz,Windows 2003,SQL Server 2005 SP4。總共進(jìn)行10次試驗(yàn),每次的取值都為1~10,試驗(yàn)耗時(shí)結(jié)果見表1。
表1 試驗(yàn)耗時(shí)結(jié)果(單位:s)
上述結(jié)果表明,窗口值的浮動(dòng)對(duì)運(yùn)行時(shí)間的影響極小,每次運(yùn)行的時(shí)間差異非常小,耗時(shí)差最大值也僅為58-48 = 10 s。這跟運(yùn)行環(huán)境緊密相關(guān),試驗(yàn)的服務(wù)器同時(shí)運(yùn)行多個(gè)虛擬機(jī),因此其他虛擬機(jī)的服務(wù)對(duì)CPU、內(nèi)存和硬盤的使用都會(huì)影響試驗(yàn)結(jié)果。圖2可以更直觀地表述相關(guān)結(jié)果。
圖2 試驗(yàn)耗時(shí)結(jié)果
通過(guò)上文可以得出結(jié)論:對(duì)窗口大小的設(shè)置不影響實(shí)用方案的選擇。根據(jù)多年的經(jīng)驗(yàn)積累,最終實(shí)際選擇
= 3。
無(wú)效數(shù)據(jù)基本都是信息不全造成的,在補(bǔ)齊信息后即可轉(zhuǎn)換為有效數(shù)據(jù)。因?yàn)榍懊? 300個(gè)有效數(shù)據(jù)已經(jīng)安排完畢,所以此時(shí)不再由SQL查詢語(yǔ)句調(diào)用存儲(chǔ)過(guò)程,而是業(yè)務(wù)層通過(guò)業(yè)務(wù)邏輯層傳遞參數(shù)調(diào)用存儲(chǔ)過(guò)程,完成單個(gè)數(shù)據(jù)的存儲(chǔ)位置安排任務(wù)。
該文所闡述的算法來(lái)源于普通的招考錄取社會(huì)現(xiàn)象,根據(jù)多年的實(shí)踐最終抽象為“標(biāo)準(zhǔn)容器存儲(chǔ)對(duì)象”的問(wèn)題。
在解決該問(wèn)題前,都采用傳統(tǒng)的人工方式分配位置。當(dāng)對(duì)象數(shù)量大約為2 000個(gè)時(shí),工作人員需要3個(gè)工作日才能完成任務(wù),而且必須是全程只處理這一件事,不能同時(shí)處理其他事務(wù)。當(dāng)對(duì)象數(shù)量大于3 000個(gè)時(shí),通常工作人員需要4~5個(gè)工作日才能完成。在實(shí)際運(yùn)行中,該流程所許用的時(shí)間常少于4個(gè)工作,工作人員必須在非工作時(shí)間繼續(xù)處理這個(gè)問(wèn)題。
在提出該算法后,真正的處理時(shí)間小于1 min,提升了工作效率。即使是性能并不出眾的服務(wù)器仍然可以在非常短的時(shí)間內(nèi)達(dá)到預(yù)期結(jié)果。