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