前言
在數據庫系統中,大對象往往是指這樣一種類型的數據:這些數據的體積較大,占用的字節數在KB級或MB級,有的甚至達到GB級。對于DBMS來說,這些數據不具備結構性,只是單純的字節流,存儲時需一次性存入,讀取時需一次性取出。日常生活中常見的圖片、事頻、音頻以及大數據量大文本文件,都屬于大對象數據的范疇?,F有的各種關系型數據庫管理系統也都提供了豐富的數據類型和完備的存取方法來管理大對象數據。毫無疑問,作為大型通用的數據庫管理系統,達夢數據庫為大對象的存儲類型提供了全面的支持,在大對象數據的存儲組織方式上采用B樹結合從表的方式達到了很好的存取效率。然而,與Oracle的大對象存取效率相比,達夢數據庫還有差距。為此,達夢數據庫開發部門在近期對達夢數據庫的存取策略進行了改進和優化。測試結果表明,優化后的達夢數據庫在大對象存取效率方面與Oracle相比,要略微勝出。本文詳細討論了達夢數據庫大對象存取優化的方法和思路,其目的主要有兩個:(1)為使用達夢數據庫的廣大開發人員詳細解釋系統中大對象的存取方法,使他們能夠基于達夢數據庫開發出高效的應用系統;(2)拋磚引玉,向廣大開發人員介紹達夢數據庫,希望能夠引起更多人的關注,獲得更多的改進建議。
1.1 大對象存儲組織方式及其不足
不同于Oracle的簇存儲方式,達夢數據庫中,大對象數據的存儲結構采用和普通數據一樣的B樹結構。對于每一個具有大對象字段的表(稱之為主表),系統會為其設置一個從表,此表專門用來存儲這個表的大對象數據。
當往主表插入一條含大對象的記錄時,系統將判斷此記錄的大小是否超過950字節,如果小于950字節,那么系統將這條記錄直接插入到主表之中;反之,將這條記錄中的大對象數據插入到從表之中。系統中同時規定,從表中每條記錄的DATA字段的最大值為950個字節,如果一個大對象數據超過950B,那么需要將這個大對象劃分成若干分片,每個數據分片表1中的其余三個字段組成一條從表記錄插入到從表中。
原有系統的這種做法簡單易行,在基本的存儲模塊上增加了分片模塊和重新組裝模塊即可實現了對大對象的存取。然而,這種做法有兩個缺陷。
1.從表記錄中數據分片允許的最大值太小(為950個字節),導致一個體積較大的大對象在存儲時,需要被分解成很多分片,在提取該大對象時,又需要重新組裝眾多分片。這些操作將耗費大量CPU時間。
2.數據分片的最大值太小還導致了空間利用率過低。達夢數據庫中,從表中的ROWID,COLID,FRAGID字段長度分別為8,2,4字節,每條物理記錄另有控制信息26字節。這樣,從表中每個數據分片需要有40個字節的數據來進行標識。假設需要往某主表插入100萬個大小為1K的大對象,由于1KB > 950B,每個大對象都需要拆成兩條記錄插入到從表中。簡單計算可以得出,存儲每個大對象需要的額外字節為80B,100萬個大對象,共1GB大約需要80MB額外空間,將近有10%的空間被用來標識大對象的數據分片,由此可以看出空間浪費較為嚴重。
將數據分片的最大值設定為950字節是沒有必要的??梢詫⑦@個值適當放大,減少將大對象分片重新組裝分片消耗的時間,以及提高空間利用率。
1.2 大對象的寫日志機制及其不足
達夢數據庫中日志包括redo日志和undo日志。目前的系統中,在插入大對象數據時,redo日志的生成和處理方式和一般的數據插入是完全一樣的,undo日志則進行了優化。下面首先對這兩種機制進行簡要的介紹。
1.2.1 redo日志的處理
達夢數據庫中的redo日志記錄了系統的對數據塊的更新動作,我們可以按照redo日志,重做系統的每一步操作,保證系統的一致性。在系統正常工作時,每當往表中插入一條物理記錄時,便會生成一條redo日志。在一個事務提交之前,系統將首先把系統緩沖區中的redo日志寫入到磁盤中,然后再提交事務。通過這種方式,可以保證系統發生故障的時候,能夠通過redo日志來恢復數據,保證系統數據的完整性和一致性。
對于每一條要插入的物理記錄,在將其插入到數據頁之前都會構造一條對應的redo日志記錄,redo日志記錄的結構是一個六元組:
(dbid, file_id, page_no, offset, len, new_data)
其中,前4個參數是為了標識日志記錄對應的物理記錄在文件中所處的數據塊的地址和塊內偏移,參數new_data為正式數據,保存物理記錄的記錄頭+記錄體信息。參數len表示new_data數據的長度。
系統在每次故障后重啟時,使用上次運行時在日志文件中產生的redo日志記錄,根據記錄中dbid, file_id, page_no, offset四個字段的信息,找到記錄對應的物理記錄所在的數據塊中的地址,并用new_data來覆蓋這個物理記錄。這樣就可以保證系統故障前提交的事務對數據庫的更新操作得到執行。
1.2.2 undo日志的處理
達夢數據庫中,undo日志機制可以確保系統崩潰后重啟時,系統能消除未提交事務對系統造成的影響。系統為每個事務分配有若干回滾段,事務對數據進行修改操作時會在回滾段中生產相應的回滾記錄。如果事務成功提交,回滾段將被拋棄,如果事務被回滾,系統將利用回滾段將數據庫中的數據恢復到事務開始前的狀態。為了在系統故障或掉電等情況下,回滾段中的數據不會丟失,回滾段的數據記錄在數據庫文件而不是在內存中。
系統對插入大對象時的undo日志生成方法進行了優化。對于普通數據,每往表中插入一條記錄,系統會為記錄生成回滾記錄并保存到回滾段中;對于大對象數據,將大對象分片并插入到從表時,對于從表中具有同樣rowid和colid的記錄,僅對fragid = 0的記錄生成回滾記錄。在回滾時,根據回滾記錄上rowid和colid,對從表的所有具有相同rowid和colid的記錄進行回滾。顯然,這種做法能夠保證插入操作的正確回滾。
綜上所述,系統在往從表插入大對象數據時,redo日志的生成方法和普通數據是完全一樣的,而undo日志則進行了優化??梢越Y合大對象數據插入的特點,參考undo日志在這方面的優化方法,來對大對象插入的redo日志生成機制進行優化。
2.1 大對象存儲組織的優化
原有系統將從表中大對象數據的體積的最大值限定為950字節是不合理的??梢钥紤]將這個值適當擴大。顯然,從表記錄允許存放的大對象體積越大,大對象存取的時間越短,空間利用率也越高。為此,可以考慮將從表中大對象體積的最大值放大到一個極限的位置。下面討論這個極限值如何確定。
達夢數據庫對記錄大小的限制是,系統內部任何物理記錄的最大值不能超過塊長一半。這種規定是為了確保一條物理記錄只在一個物理塊中,保證一個B樹節點至少有兩條物理記錄,避免B樹退化成鏈表結構。由此可知,從表中記錄的大小不能超過塊長一半。由于大對象從表的結構中,前三個字段的大小取值是固定的,因此,在從表記錄大小不超過塊長一半的前提下,能夠精確地計算出從表記錄中大對象體積的最大值。計算方法在此從略。顯然,通過擴大從表記錄中大對象體積的最大值,可以減少大對象分片和分片重組的時間,提高空間利用率。
2.2 大對象寫日志機制的優化
分析寫日志機制可以看出,插入大對象數據到從表中時,為每一條物理記錄生成完整的redo日志記錄是沒有必要的。一種可行的優化思路是:
1.在redo日志記錄中只保存從表物理記錄的rowid,colid和fraid字段,而不需要DATA字段既大對象數據。這種做法由于不用保存大對象數據,減少了redo日志的數據量,提高了寫日志的速度,進而減少大對象數據插入所消耗的時間。
2.記錄插入過程中,保存了從表物理記錄的數據塊的塊號,在大對象插入結束之后,事務提交之前,首先將插入操作生成的redo日志刷盤,然后將這些塊刷盤。
顯然,這種redo日志優化方法將提高大對象插入的效率。下面從對事務的redo和undo操作兩個方面來說明這種優化方法可以保證數據的正確性。
1.對于在故障前已提交的事務,故障恢復時需要恢復而不是撤銷事務對數據庫的更新操作。在系統重啟后,由于上次系統運行時往從表中插入的物理記錄已經在事務提交之前被刷盤,所以此時(既系統重啟后)從表中的記錄都是完整和正確的,此時再使用redo日志記錄來覆蓋物理記錄,只不過是將物理記錄的前三個字段重新寫入數據塊而已,不會造成數據的丟失,雖然redo日志記錄并沒有記錄大對象數據。
2.對于在故障前未提交的事務,故障恢復時需要撤銷事務對數據庫的改動。此時,首先打開redo日志文件,從事務的開始點出發,正向掃描redo日志文件,使用redo日志記錄中的rowid,colid和fraid替換表中的相應物理記錄的這三個字段,直到故障發生的地方停止替換。然后再使用undo日志文件從故障發生的地方開始,反向利用回滾記錄將具有與回滾記錄相同rowid和colid的物理記錄恢復到事務開始前的狀態。這樣,事務對從表的插入操作被成功撤銷。
由此可見,采用這種方法完全可以在系統發生故障后保證數據的正確性。這種優化方案是完全可行的。優化方案的具體做法如下:
(1)修改redo日志生成機制。優化前的系統對從表的insert操作,為物理頁上的所有改動生成日志記錄并寫入日志文件。優化后,RREC記錄上只記錄不包含分片數據的其他修改;
(2)將大對象插入到從表時記錄被修改的從表數據頁號;
(3)往從表插入一個大對象結束以后,將redo日志刷盤;
(4)將redo日志刷盤后,將插入過程中修改的從表數據頁全部刷盤。
2.3 寫日志機制優化的補充說明
完成redo日志優化的優化工作后,在性能測試中我們發現,對redo日志生成機制進行優化并不能百分百地減少大對象插入的時間。當插入的大對象數據量較小時,優化后插入所消耗的時間反而要大于優化前。出現這種現象的原因是,優化后,每插入一個大對象數據都需要將從表中的相應數據頁刷盤,刷盤將引起大量I/O操作,這將為日志生成增加額外的時間,數據量小的情況下,增加的時間將超過由于優化減少的時間。
針對這個問題,我們設計了以下解決方案:
(1)由應用系統開發人員來決定是否需要對大對象存儲進行日志生成優化。如果需要存儲的大對象數據量不是很大(建議值為小于100K),可以指定不進行日志優化,如果需要存儲的大對象數據量比較大,可以指定進行日志優化。
(2)系統中增加一個存儲過程,用戶可以調用此存儲過程來指定是否對日志生成進行優化。
(3)如果要插入的大對象數據類型是TEXT,系統默認不對日志進行優化,如果插入的大對象數據類型是BLOB,系統默認對日志進行優化。
最后給出優化前后的測試對比結果。由于本次優化工作的目標是縮小DM5.6相對于Oracle9i在大對象存取效率上的差異,因此首先給出優化前的DM5.6與Oracle9i的大對象存取速度對比結果,然后給出DM5.6優化前后的大對象存取速度對比結果。通過比較Oracle9i和優化后的DM5.6在在大對象存取速度上相對于優化前DM5.6的提升率,可以間接比較Oracle9i和優化后的DM5.6在大對象存取上的時間效率的差異。
3.1 優化前DM與Oracle的大對象存取性能對比
(1)相關配置說明
硬件配置:
CPU(個數) Pentium cpu 2.66GHz
內存 1G
操作系統 WINXP
數據庫版本:
ORACLE ORACLE 9i
DM 優化前的DM5.6
內部參數:
ORACLE 默認配置
DM 默認配置
測試數據:
大對象數據采用大小為5M 的MPG格式文件。測試時,分別往數據庫中插入1-64條,共5-320MB大對象數據。測試結果取三次的平均值。
(2)ODBC方式插入大對象
測試程序采用ODBC方式連接數據庫,每插入一條數據便提交一次事務。首先給出優化前的DM5.6與Oracle9i的大對象插入速度對比結果。
圖1 優化前的DM5.6和Oracle9i的大對象插入速度對比
(3)ODBC方式讀取大對象
接下來給出優化前的DM5.6與Oracle9i的大對象讀取速度對比結果。
圖2 優化前的DM5.6和Oracle9i的大對象讀取速度對比
3.2 優化前后DM大對象存取性能對比
(1)相關配置說明
硬件配置:
CPU(個數) 酷睿雙核1.6GHz
內存 1G
操作系統 WINXP
數據庫版本:
DM 優化前的DM5.6
DM 優化后的DM5.6
內部參數:
ORACLE 默認配置
DM 默認配置
測試數據:
在內存中生成1MB的大對象數據,測試時往數據庫中插入1-500條大對象記錄。測試結果取三次的平均值。
(2)ODBC方式插入大對象
測試程序采用ODBC方式連接數據庫,每插入一條數據便提交一次事務。首先給出優化前后的DM5.6大對象插入速度對比結果。
圖3 優化前后的DM5.6大對象插入速度對比
(3)ODBC方式讀取大對象
接下來給出優化前后的DM5.6大對象讀取速度對比結果。
圖4 優化前后的DM5.6大對象讀取速度對比
3.3 結果分析
通過四個對比測試可以得出以下結論:
(1)優化前的DM5.6與Oracle9i在大對象插入速度上相差不大,平均來說,Oracle9i要快10%左右;但是在讀取速度上,優化前的DM5.6要比Oracle9i慢接近1倍;
(2)優化后的DM5.6在大對象插入速度有大幅提升,從圖3可以看到,優化后的DM5.6要比優化前快約30%左右;在讀取速度上,優化后的DM5.6比優化前快90%左右,接近1倍,基本上與Oracle9i的讀取速度基本持平。
最后的結論是,此次優化工作取得了令人滿意的結果。
新聞熱點
疑難解答