馬士振 林向東 白永福 張紅旗 馮 剛
(中國(guó)北京 100080 北京市地震局)
測(cè)震臺(tái)網(wǎng)的一項(xiàng)重要工作是產(chǎn)出地震觀測(cè)目錄。作為一種重要的觀測(cè)數(shù)據(jù),在地震預(yù)測(cè)預(yù)報(bào)方面,地震觀測(cè)目錄可以用來(lái)研究中強(qiáng)地震前地震空區(qū)及條帶的分布情況(楊芬等,2010);在地震應(yīng)急方面,地震觀測(cè)目錄可以為有關(guān)部門(mén)提供地震的三要素信息,服務(wù)于應(yīng)急救災(zāi)工作。
自“十五”網(wǎng)絡(luò)項(xiàng)目以來(lái),北京市測(cè)震臺(tái)網(wǎng)已積累了近萬(wàn)條地震目錄。為了更好的服務(wù)于行業(yè)和社會(huì)公眾,測(cè)震臺(tái)網(wǎng)開(kāi)發(fā)了基于Django框架的B/S結(jié)構(gòu)的地震目錄服務(wù)系統(tǒng),以提供地震目錄的快速展示及目錄數(shù)據(jù)的圖像化服務(wù)。
在用戶請(qǐng)求數(shù)據(jù)服務(wù)時(shí),將數(shù)據(jù)一次性全部交付給用戶是不合適的,因?yàn)檫@不但讓用戶等待時(shí)間較長(zhǎng),而且浪費(fèi)寶貴的網(wǎng)絡(luò)資源(李光耀等,2004);然而,在網(wǎng)絡(luò)上傳輸小塊數(shù)據(jù),則既可以減小網(wǎng)絡(luò)流量,又可以提高網(wǎng)頁(yè)的響應(yīng)速度,還可以有效降低服務(wù)器負(fù)載(王瑞波,2009)。因此,在構(gòu)建Web數(shù)據(jù)服務(wù)系統(tǒng)時(shí),為了提高系統(tǒng)的數(shù)據(jù)服務(wù)性能,可以采用把數(shù)據(jù)分批傳送給用戶的技術(shù),即數(shù)據(jù)的分頁(yè)技術(shù)。
Django來(lái)源于一個(gè)可滿足每天數(shù)百萬(wàn)次頁(yè)面查看請(qǐng)求的在線報(bào)紙服務(wù),是采用Python 語(yǔ)言驅(qū)動(dòng)的Web 應(yīng)用程序框架,可以快速開(kāi)發(fā)數(shù)據(jù)庫(kù)驅(qū)動(dòng)網(wǎng)站,有著開(kāi)源、免費(fèi)、敏捷開(kāi)發(fā)的特點(diǎn)。使用Django框架,可以節(jié)省開(kāi)發(fā)周期,并且便于維護(hù)和升級(jí)(程文芳等,2013)。Django廣泛應(yīng)用于博客系統(tǒng)(楊志慶,2013)、資源共享平臺(tái)(程文芳等,2013)、數(shù)據(jù)庫(kù)快速查詢(齊金剛等,2014)等Web應(yīng)用的開(kāi)發(fā)工作中。
結(jié)合地震目錄服務(wù)系統(tǒng)的開(kāi)發(fā)實(shí)踐,作者使用Django框架的Paginator函數(shù)和MySQL數(shù)據(jù)庫(kù)的LIMIT子句,實(shí)現(xiàn)了兩種Web地震目錄的快速分頁(yè)方法,可以有效提高Web頁(yè)面上數(shù)據(jù)服務(wù)的響應(yīng)速度。
圖1 Web查詢執(zhí)行過(guò)程示意Fig.1 Schematic diagram of the implementation process of Web data query
在網(wǎng)絡(luò)環(huán)境中,獲取數(shù)據(jù)庫(kù)中數(shù)據(jù)的性能主要受到數(shù)據(jù)庫(kù)的查詢代價(jià)和網(wǎng)絡(luò)數(shù)據(jù)傳輸能力的約束。因此,在遠(yuǎn)程網(wǎng)絡(luò)壞境中,為了提高獲取數(shù)據(jù)的性能,主要依靠減少傳輸?shù)拇螖?shù)和數(shù)據(jù)量來(lái)解決(王瑞波,2009)。在進(jìn)行Web數(shù)據(jù)查詢時(shí),其執(zhí)行過(guò)程可簡(jiǎn)化為一個(gè)3層的網(wǎng)絡(luò)結(jié)構(gòu)。由于這3層分別在不同的網(wǎng)絡(luò)環(huán)境中,因此實(shí)現(xiàn)Web數(shù)據(jù)查詢分頁(yè)可以在3個(gè)不同層次中進(jìn)行(齊金剛等,2014),見(jiàn)圖1。
Web服務(wù)器和數(shù)據(jù)庫(kù)服務(wù)器將滿足用戶查詢條件的數(shù)據(jù)一次性發(fā)送給用戶,瀏覽器在用戶顯示時(shí)進(jìn)行分頁(yè)(齊金剛等,2014)。這種分頁(yè)方式雖然減少了數(shù)據(jù)傳輸次數(shù),但增加了傳輸?shù)臄?shù)據(jù)量,延長(zhǎng)了用戶的等待時(shí)間。因此,這種分頁(yè)只能帶來(lái)瀏覽的便利,對(duì)查詢性能的改善沒(méi)有幫助。本文中不再討論。
數(shù)據(jù)庫(kù)服務(wù)器把符合條件的查詢結(jié)果一次性發(fā)送給Web服務(wù)器,由其對(duì)查詢結(jié)果進(jìn)行分頁(yè)操作。然后,根據(jù)客戶端請(qǐng)求,Web服務(wù)器將對(duì)應(yīng)的頁(yè)面數(shù)據(jù)發(fā)送給客戶瀏覽器(齊金剛等,2014),見(jiàn)圖2。一般來(lái)講,Web服務(wù)器與數(shù)據(jù)庫(kù)服務(wù)器在一個(gè)局域網(wǎng)內(nèi),兩者間的傳輸速度較客戶瀏覽器到Web服務(wù)器間快。因此,相對(duì)于客戶端分頁(yè)方式,采用這種分頁(yè)方法,客戶瀏覽器將獲得較快的響應(yīng),而且實(shí)現(xiàn)方便。
在數(shù)據(jù)庫(kù)層分頁(yè)的目標(biāo)是:通過(guò)減少3層之間的數(shù)據(jù)流量以提高系統(tǒng)的查詢性能,即數(shù)據(jù)庫(kù)服務(wù)器每次只向Web服務(wù)器返回需要顯示的數(shù)據(jù)記錄,而后由Web服務(wù)器向用戶提交這些數(shù)據(jù)(王博等,2006),見(jiàn)圖3。目前,在數(shù)據(jù)庫(kù)服務(wù)層實(shí)現(xiàn)分頁(yè)的方法主要有兩種:①利用數(shù)據(jù)庫(kù)自身提供的分頁(yè)方法;②調(diào)用存儲(chǔ)過(guò)程。考慮到全國(guó)大部分測(cè)震臺(tái)網(wǎng)使用的數(shù)據(jù)庫(kù)是MySQL,而MySQL提供了便捷的limit子句來(lái)實(shí)現(xiàn)數(shù)據(jù)分頁(yè)功能,因此本文介紹數(shù)據(jù)庫(kù)自身提供的分頁(yè)方法。
圖2 Web層分頁(yè)查詢過(guò)程示意Fig.2 Schematic diagram of the implementation process of pagination in Web layer
圖3 數(shù)據(jù)庫(kù)層分頁(yè)查詢過(guò)程示意Fig.3 Schematic diagram of the implementation process of pagination in database layer
在初步完成的地震目錄服務(wù)系統(tǒng)中,使用Django框架的Paginator函數(shù),對(duì)Web服務(wù)層的分頁(yè)功能進(jìn)行測(cè)試。通過(guò)為Paginator函數(shù)提供每頁(yè)顯示的地震目錄行數(shù),該函數(shù)自動(dòng)對(duì)全部數(shù)據(jù)進(jìn)行分頁(yè)操作。分頁(yè)操作完成后,即可使用Paginator對(duì)象的方法獲得分頁(yè)總數(shù)、頁(yè)數(shù)列表、每個(gè)分頁(yè)的具體地震目錄等對(duì)象。而后,再將上述對(duì)象渲染到模板文件中,也就實(shí)現(xiàn)了地震目錄數(shù)據(jù)在Web服務(wù)層的數(shù)據(jù)分頁(yè)功能。在地震目錄服務(wù)系統(tǒng)中,實(shí)現(xiàn)分頁(yè)的核心代碼如下。
from django.core.paginator import Paginator # 導(dǎo)入Paginator分頁(yè)函數(shù)from django.db import connection # 導(dǎo)入數(shù)據(jù)庫(kù)連接函數(shù)from django.shortcuts import render # 導(dǎo)入模板渲染函數(shù)def historyCataList(req)∶ # 定義視圖函數(shù)
cursor = connection.cursor() # 建立數(shù)據(jù)庫(kù)連接,獲取游標(biāo)
eqkCount = cursor.execute(“SELECT * FROM Catalog # 執(zhí)行數(shù)據(jù)查詢操作
ORDER BY O_time ”) # O_time 是主鍵
cata_All = cursor.fetchall() # 獲取查詢結(jié)果
cursor.close() # 關(guān)閉游標(biāo)
connection.close() # 關(guān)閉連接
eqkPage = Paginator(cata_All,10) # 對(duì)查詢結(jié)果分頁(yè),每頁(yè)顯示10條目錄pageListCount = eqkPage.num_pages # 分頁(yè)總數(shù)
pageList = eqkPage.page_range # 頁(yè)數(shù)的列表
page1 = eqkPage.page(1) # 獲得第一頁(yè)的地震目錄
# 將查詢結(jié)果渲染到模板文件
return render(req,'historyCataList.html',{'eqkData'∶page1,
’pageListCount’∶pageListCount,’pageList’∶pageList})
此方法借助Django提供的函數(shù),實(shí)現(xiàn)了在Web服務(wù)層對(duì)查詢結(jié)果的分頁(yè)。通過(guò)對(duì)性能測(cè)試,發(fā)現(xiàn)當(dāng)數(shù)據(jù)量較小時(shí),客戶端的響應(yīng)速度比較快;當(dāng)數(shù)據(jù)量很大時(shí),數(shù)據(jù)庫(kù)需要較長(zhǎng)時(shí)間才能查詢到全部結(jié)果,然后將結(jié)果返回到Web服務(wù)層進(jìn)行分頁(yè)處理,最后把分頁(yè)后的數(shù)據(jù)渲染到客戶瀏覽器。在這個(gè)分頁(yè)過(guò)程中,查詢的次數(shù)少了,但數(shù)據(jù)庫(kù)查詢的工作量和網(wǎng)絡(luò)負(fù)載變大了,而且,比較大的數(shù)據(jù)量還會(huì)消耗Web服務(wù)器的存儲(chǔ)空間。因此,對(duì)于這種分頁(yè)方法,較小數(shù)據(jù)量時(shí)可用;當(dāng)數(shù)據(jù)量上升到一定量級(jí)時(shí),Web查詢性能降低。性能測(cè)試數(shù)據(jù)見(jiàn)表1。
表1 測(cè)試結(jié)果Table 1 The result of test
區(qū)域測(cè)震臺(tái)網(wǎng)使用的JOPENS地震臺(tái)網(wǎng)數(shù)據(jù)處理系統(tǒng),采用流行的MySQL數(shù)據(jù)庫(kù)對(duì)數(shù)據(jù)進(jìn)行管理。該數(shù)據(jù)庫(kù)提供limit查詢子句,用于強(qiáng)制select 語(yǔ)句返回指定的記錄數(shù)。limit接受一個(gè)或兩個(gè)數(shù)字參數(shù),參數(shù)必須是一個(gè)整數(shù)常量。如果給定兩個(gè)參數(shù),第1個(gè)參數(shù)指定第1個(gè)返回記錄行的偏移量,第2個(gè)參數(shù)指定返回記錄行的最大數(shù)目。需要注意的是,初始記錄行的偏移量是 0,而不是 1。如“select * from table limit 5,10;”返回檢索記錄行 6—15的結(jié)果。在使用limit子句時(shí),一個(gè)需要解決的問(wèn)題是確認(rèn)新的查詢從哪一行開(kāi)始,根據(jù)計(jì)算結(jié)果可知,第N頁(yè)的起始位置應(yīng)為:(第N頁(yè)的頁(yè)碼-1)*每頁(yè)顯示的行數(shù)。由此,可以得知limit子句的格式應(yīng)為:“l(fā)imit (N頁(yè)的頁(yè)碼-1)*每頁(yè)顯示的行數(shù),每頁(yè)顯示的行數(shù)”。
在地震目錄服務(wù)系統(tǒng)中,Web應(yīng)用程序?qū)琇IMIT子句的SQL語(yǔ)句傳遞給數(shù)據(jù)庫(kù)服務(wù)器,即可實(shí)現(xiàn)查詢結(jié)果的分頁(yè)。以下為使用LIMIT子句實(shí)現(xiàn)數(shù)據(jù)庫(kù)層分頁(yè)的核心代碼。
from django.db import connection # 導(dǎo)入數(shù)據(jù)庫(kù)連接函數(shù)from django.shortcuts import render # 導(dǎo)入模板渲染函數(shù)def historyCataList(req,pageId)∶ # 定義視圖函數(shù)
cursor = connection.cursor() # 建立數(shù)據(jù)庫(kù)連接,獲取游標(biāo)
currentPageNo = int(pageId) # 獲得當(dāng)前頁(yè)頁(yè)碼
eqkPerPage = 10 #設(shè)置每頁(yè)顯示地震目錄條數(shù)
# 生成分頁(yè)查詢語(yǔ)句,O_time是主鍵
queryLine =‘SELECT * FROM Catalog ORDER BY O_time LIMIT ‘ +
str((currentPageNo-1)*eqkPerPage) + ',' + str(eqkPerPage)
queryCountLine = ‘SELECT count(*) FROM Catalog’cursor.execute(queryLine) # 執(zhí)行分頁(yè)查詢
cata_all = cursor.fetchall() # 獲得查詢得到的分頁(yè)數(shù)據(jù)
cusor.execute(queryCountLine) # 執(zhí)行獲取條目總數(shù)查詢
eqkCount = cursor.fetchone() # 獲得符合條件的條目總數(shù)量cursor.close() # 關(guān)閉游標(biāo)connection.close() # 關(guān)閉連接
if (eqkCount[0] % eqkPerPage == 0)∶ # 獲得總的頁(yè)數(shù)
pageListCount = eqkCount [0]/ eqkPerPage else∶
pageListCount = eqkCount [0]/ eqkPerPage + 1
pageList = range(pageListCount) # 獲得頁(yè)數(shù)的列表
# 將查詢結(jié)果渲染到模板文件
return render(req,'historyCataList.html',{'cata_All'∶cata_All,
'pageListCount'∶pageListCount,'pageList'∶pageList})
此方法使用MySQL自帶LIMIT子句,實(shí)現(xiàn)在數(shù)據(jù)庫(kù)服務(wù)層對(duì)查詢結(jié)果的分頁(yè),即請(qǐng)求哪一頁(yè),就查詢哪一頁(yè),然后給客戶端返回哪一頁(yè)。采用該分頁(yè)方法,減小了數(shù)據(jù)庫(kù)服務(wù)器的工作量,降低了網(wǎng)絡(luò)流量。通過(guò)對(duì)該方法的性能測(cè)試,發(fā)現(xiàn)當(dāng)數(shù)據(jù)量較大時(shí),客戶端的響應(yīng)速度依然比較快(表1)。
在局域網(wǎng)中對(duì)Web服務(wù)層分頁(yè)方法和數(shù)據(jù)庫(kù)服務(wù)層分頁(yè)方法進(jìn)行性能時(shí)比測(cè)試:要求每頁(yè)顯示10條地震目錄,地震目錄的檢索位置為第100—100 000條,采用Firefox瀏覽器的firebug進(jìn)行查詢耗時(shí)統(tǒng)計(jì)。測(cè)試結(jié)果見(jiàn)表1。
兩種分頁(yè)方法性能測(cè)試的結(jié)果(表1)表明,當(dāng)查詢數(shù)據(jù)量較小時(shí),兩者的分頁(yè)用時(shí)接近。此時(shí),由于在Web服務(wù)層實(shí)現(xiàn)地震目錄的分頁(yè)比較簡(jiǎn)單,可以考慮在Web服務(wù)層實(shí)現(xiàn)分頁(yè)操作;當(dāng)查詢數(shù)據(jù)上升到萬(wàn)條或10萬(wàn)條量級(jí)時(shí),兩者的響應(yīng)速度出現(xiàn)較大數(shù)據(jù)庫(kù)層差異:在數(shù)據(jù)庫(kù)層進(jìn)行分頁(yè)用時(shí)更少,頁(yè)面的響應(yīng)速度更快。在北京市測(cè)震臺(tái)網(wǎng)的地震目錄服務(wù)系統(tǒng)中,目前采用數(shù)據(jù)庫(kù)層分頁(yè)方法。對(duì)于北京市測(cè)震臺(tái)網(wǎng)近萬(wàn)條地震目錄分頁(yè),數(shù)據(jù)庫(kù)層方法的查詢效率更高。因此,當(dāng)有關(guān)地震臺(tái)網(wǎng)需要開(kāi)發(fā)地震目錄數(shù)據(jù)服務(wù)系統(tǒng)時(shí),可以根據(jù)本臺(tái)網(wǎng)數(shù)據(jù)庫(kù)中地震目錄的記錄數(shù)量,酌情選擇分頁(yè)方法,以更好更快地提供地震目錄查詢服務(wù)。
在本文撰寫(xiě)過(guò)程中,獲得王晨高級(jí)工程師和張英博士的支持,在此表示感謝。
程文芳,張潔,夏明一,張北辰.極地標(biāo)本資源共享平臺(tái)系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)[J]. 極地研究,2013,25(2):185-196.
李光耀,易虎,李波. 基于存儲(chǔ)過(guò)程分頁(yè)優(yōu)化Web數(shù)據(jù)查詢性能[J]. 微計(jì)算機(jī)應(yīng)用,2004,25(4):475-479.
齊金剛,李滔,李晉軍. Django框架Web數(shù)據(jù)查詢分頁(yè)技術(shù)研究[J]. 電子設(shè)計(jì)工程, 2014,22(5):33-37.
王博,任濤. Web數(shù)據(jù)庫(kù)分頁(yè)瀏覽方法性能分析[J]. 現(xiàn)代電子技術(shù), 2006, 29(10):68-70.
王瑞波. 一種分頁(yè)查詢優(yōu)化方法的研究與實(shí)現(xiàn)[D]. 北京:北京化工大學(xué),2009:4.
楊芬,付虹. 滇西北東條帶地震圍空及條帶與中強(qiáng)地震的時(shí)空關(guān)系[J]. 地震地磁觀測(cè)與研究, 2010,31(5):7-12.
楊志慶. 基于Django的Blog系統(tǒng)的開(kāi)發(fā)與實(shí)現(xiàn)[J]. 機(jī)電一體化, 2013,9:69-72.