程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> DB2數據庫 >> DB2教程 >> DB2 9中15個pureXML性能最佳實踐

DB2 9中15個pureXML性能最佳實踐

編輯:DB2教程

DB2 9引入了pureXML支持,這意味著XML數據將以其固有的層次格式被存儲和查詢。為了查詢 XML數據,DB2 提供了兩種語言,SQL/XML和XQuery。此外,DB2 9 還具有成熟的XML 索引功能和對 XML 模式驗證的支持。雖然大多數已有的關於DB2性能的指南同樣適用於XML數據,但是本文還將提供其他一些特定於XML的性能方面的提示。

簡介

DB2 9中的pureXML支持為管理XML數據提供了有效的、多方面的功能。對於很多XML應用程序而言,性能是高度優先考慮的一個方面。DBA和應用程序設計人員都可以通過他們份內的工作來確保良好的性能。首先,我們有關於DB2 各個方面的所有傳統的性能指南,包括 CPU/內存/磁盤配置的平衡、表空間和緩沖池的調優、鎖、日志記錄、查詢執行計劃等。所有這些話題在之前的DB2 文章裡都曾論述過(見參考資料),並在管理 DB2中的XML數據時仍然適用。

幸運的是,這些問題當中有很多問題是由 DB2的自治存儲和自調優內存管理等自治功能來處理的。它們為很多應用程序提供了高水平的性能,要求的手動干預很少。但是,具有更高性能需求的XML應用程序還可以從其他性能方面的考慮當中受益。本文集中討論這方面的情形,同時給出為DB2 9中與 XML 相關的應用程序取得最佳性能的提示和指南。

本文將討論和闡述15個XML性能提示(排序不分先後)。這15個提示涵蓋了很多領域,但是經驗表明,存在性能問題的應用程序通常只需要應用其中一兩個提示就能達到所需的性能。

提示1:理智選擇XML 文檔的粒度。

提示2:為了取得更好的XML性能,使用DMS和更大的頁。

提示3:必要時,如何將XML數據放入一個單獨的表空間中。

提示4:如何配置 DB2,以便快速地成塊插入XML數據。

提示5:使用新的快照監視器元素檢查XML性能。

提示6:了解 XML 模式驗證的開銷。

提示7:在XPath 表達式中,盡可能使用全限定路徑。

提示8:定義傾斜的XML 索引,並避免為任何東西都建索引。

提示9:將文檔過濾謂詞放入XMLEXISTS中,而不是放入XMLQUERY中。

提示10:使用方括號 [ ]來避免 XMLEXISTS中的Boolean 謂詞。

提示11:使用RUNSTATS 收集 XML數據和索引的統計信息。

提示12:如何使用SQL/XML 發布視圖將關系數據暴露為XML。

提示13:如何使用XMLTABLE 視圖以關系格式暴露 XML數據。

提示14:對於短小的查詢或 OLTP應用程序,使用帶參數占位符的SQL/XML語句。

提示15:避免 XML 插入和檢索期間出現代碼頁轉換。

在對這些性能提示的討論中,我們假設您熟悉基本的DB2 管理和性能實踐,並熟悉基本的DB2s pureXML 支持。例如,您應該知道 XML 列、XML 索引,以及如何用SQL/XML和 XQuery 查詢 XML數據。

DB2 XML性能提示

提示1: 理智選擇XML 文檔的粒度

當設計 XML應用程序和 XML 文檔結構時,您可能面臨將哪些業務數據放在一個單獨的XML 文檔中的選擇。例如,在下面的部門表中,我們為每個部門使用一個 XML 文檔(中等粒度)。如果部門是應用程序訪問和處理數據所依賴的主要粒度,那麼這是合理的選擇。或者,我們也可以決定將多個部門組合到一個 XML 文檔中,例如,將屬於同一個單位的部門放入一個 XML 文檔中(粗粒度)。然而,如果通常一次只處理一個部門,那麼這種粒度帶來的性能就是次優的。

表 1. 創建表 dept( unitID char(8), deptdoc XML)

unitID deptdoc WWPR <dept deptID='PR27'>
  <employee id='901'>
   <name>Jim Qu</name>
   <phone>408 555 1212</phone>
  </employee>
  <employee id='902'>
   <name>Peter Pan</name>
   <office>216</Office>
  </employee>
</dept>
WWPR <dept deptID='V15'>
  <employee id='673'>
   <name>Matt Foreman</name>
   <phone>416 891 7301</phone>
   <office>216</Office>
  </employee>
  <description>This dept supports sales world wide</description>
</dept>
S-USE ... ... ...

我們還可以決定為每個雇員使用一個 XML 文檔(細粒度),並使用一個附加的“dept” 屬性,表明他或她屬於哪個部門。如果雇員本身使用經常獨立於同部門其他雇員被訪問和處理的業務對象,那麼這會是一個很好的選擇。但是,如果應用程序常常一起處理一個部門中的所有雇員,那麼每個部門一個文檔會更好一些。

尤其是,我們不推薦將很多獨立的業務對象成批地放入一個文檔中。DB2 使用XML數據上的索引對文檔進行過濾。因此,XML 文檔的粒度越細,從基於索引的訪問中得到的好處就越大。而且,如果應用程序使用DOM解析器來攝取從DB2 檢索的XML,那麼小型文檔可以帶來更好的性能。

與 XML 文檔設計相關的一個常見的問題是,何時使用屬性,何時使用元素,以及這裡做出的選擇對性能有怎樣的影響。與其說這是一個性能問題,不如說這是一個數據建模問題。這個問題像 XML的先驅 SGML 一樣古老,而且曾經歷過熱烈的辯論,只是最後也沒有廣泛地達成一致。然而,與此有關的一個重要事實是,XML 元素比屬性更靈活,因為XML 元素可以重復和嵌套。例如,在我們的部門文檔中,我們使用一個 “phone” 元素,如果一個雇員有多個電話號碼,那麼可以讓這個元素出現多次。而且,當以後需要將電話號碼拆成數段時,這個元素還是可擴展的。即 “phone” 元素可以有表示國家代碼、區代碼、分機號等等的子元素。如果“phone” 是雇員元素的一個屬性,那麼對於每個雇員,它只能出現一次,我們也不能為之添加子元素,這將妨礙模式隨時間而變化。雖然不用屬性也可以對所有數據進行建模,但是對於預先知道不會重復(對於每個元素而言),也沒有任何子字段的數據項,使用屬性仍是最直觀的選擇。屬性有助於使 XML 變得更短,因為屬性只需一個標記,而元素則需要一對開始標記和結束標記。在DB2中,查詢、謂詞和索引定義中都可以使用屬性,就像使用元素一樣容易。由於屬性的可擴展性不如元素,DB2 可以應用某些存儲和訪問優化。這應該看作是額外的性能收益,而不是將屬性轉換成元素的決定因素,尤其是當數據建模確實傾向於使用元素的時候更是如此。

總而言之,應該根據預期的訪問的主要粒度來選擇XML 文檔的粒度。如果仍然心存猶豫,那麼傾向於較細的粒度和較小的XML 文檔要穩妥些。

提示2:為了取得更好的XML性能,使用DMS和更大的頁

數據庫管理的表空間(DMS)可以比系統管理的表空間(SMS)提供更高的性能。對於關系數據是如此,對於XML的讀和寫訪問更是如此。在DB2 9中,缺省情況下,新創建的表空間是DMS。還建議將DMS 表空間與自治存儲一起使用,使 DMS 容器可以隨需增長,而不需要人為干預。如果XML 文檔太大,一個表空間中的一個頁面容納不下,那麼 DB2 就將該文檔劃分成多個區域,然後將它們分開存儲到多個頁面上。這一點對於應用程序而言是透明的,這也使得 DB2 可以處理 2 GB的XML 文檔。

通常,每個文檔劃分成的區域越少,性能就越好,對於插入和全文搜索更是如此。如果一個文檔大小超出了一個頁面,每個文檔分成的子塊的數量取決於頁寬(4KB、8KB、16KB或32KB)。表空間的頁寬越大,每個文檔劃分成的子塊數量就越小。例如,假設一個給定的文檔要劃分到 40 個 4KB的頁面上。那麼,同樣是這個文檔,可以存儲在20 個 8KB的頁面上,或者存儲在10 個 16KB的頁面或 5 個 32KB的頁面上。如果XML 文檔遠小於所選擇的頁寬,那麼就不會浪費空間,因為多個小的文檔可以存儲在一個頁面上。

經驗法則是,為XML數據選擇一個不小於平均預期文檔大小的兩倍而且不超過 32 KB 最大值的頁寬。如果為關系數據和 XML數據,或者為數據和索引使用單一的頁寬,那麼 32KB的頁寬對於XML數據而言可能有利,但是不利於關系數據和索引的訪問。在這種情況下,16KB或8KB的頁寬對於兩者而言都是較好的選擇。

提示3:必要時,如何將XML數據放入一個單獨的表空間中

如果您做了性能分析,發現對於XML數據需要大的頁寬,而對於關系數據或索引則需要小的頁寬,那麼可以使用不同的表空間來實現這一點。當定義一個表時,可以將“long”數據定向到一個具有不同頁寬的單獨的表空間中。long數據包括 LOB和 XML數據。

下面的例子定義了兩個緩沖池和兩個表空間,一個表空間的頁寬為4KB,另一個表空間的頁寬為32KB。(注意,一個表空間總是要有一個緩沖池,且緩沖池要有一個匹配的頁寬。)表 product被分配到具有 4KB 頁面的表空間 "relData"。它的所有列被存儲在那個表空間中,只有 XML 列 "description" 存儲在表空間 "XMLdata"中的32KB的頁面上。

清單 1. 兩個緩沖池和兩個表空間的定義

create bufferpool bp4k pagesize 4k;
create bufferpool bp32k pagesize 32k;
create tablespace relData
pagesize 4K
managed by automatic storage
bufferpool bp4k;
create tablespace XMLData
pagesize 32K
managed by automatic storage
bufferpool bp32k;
create table product(pid bigint, name varchar(20), brand varchar(35),
           category integer, price decimal, description XML)
    in relData
long in XMLData;

DB2 9中的缺省表空間有所變化。除非顯式指定,否則新創建的表空間都是具有較大行ID的DMS。這意味著,一個具有 4KB 頁面的表空間可以增長到 2TB,而不是DB2 8中的64GB,具有 32KB 頁面的表空間可以增長到 16TB,而不是512GB。而且,每個頁面 255 行的限制也去掉了,32KB的頁面上允許至多 2335 行。因此,每頁的行數限制本身不再是為關系數據使用小型頁面的理由。

總而言之,在為XML數據選擇不同的表空間時,仍應按常理來考慮。更少的緩沖池和表空間,以及更少的頁寬種類,可以導致更簡單的物理數據庫設計,從而更容易管理、維護和調優。因此,要避免引入多種頁寬,除非您知道這樣做確實可以帶來性能好處。

提示4:如何配置 DB2,以便快速地成塊插入XML數據

DB2 9 支持兩種將XML數據從文件系統轉移到 DB2 表中的選項:插入和導入。從性能和調優的角度來看,這兩種選項具有類似的特征,因為導入實用程序實際上是執行一系列的插入。不管是應用程序執行批量插入(可能通過並發插入線程),還是使用導入,下面的性能指南都適用:

務必使用具有較大頁寬的DMS 表空間(見 提示2),這是一個關鍵的先決條件。

即使沒有在要成批插入數據的表上定義任何索引,DB2 pureXML 存儲機制仍然會透明地維護所謂的區域和路徑索引,以便於進行高效的XML 存儲訪問。因此,應提供足夠的緩沖池空間來支持索引讀操作。

如果表上需要多個用戶定義的XML 索引,那麼通常更好的做法是在批量插入之前定義它們,而不是事後才創建它們。在插入期間,每個 XML 文檔只被處理一次,便可以為所有 XML索引生成索引條目。然而,如果發出多條 “create index”語句,那麼 XML 列中的所有文檔將被訪問多次。

“ALTER TABLE <tablename> APPEND ON” 為表啟用追加(append)模式。新的數據被追加到表的末尾,而不是在已有的頁面上搜索空閒空間。這可以提高批量插入的運行時性能。

增加日志緩沖池大小(LOGBUFSZ)和日志文件大小(LOGFILSIZ)有助於提高插入性能。這一點對於XML 插入尤其重要,因為每一行的數據量都比關系數據大得多。建議為日志使用快速的I/O 設備。

如果使用“ALTER TABLE <tablename> ACTIVATE NOT LOGGED INITIALLY” (NLI),可以避免日志記錄。然而要注意,如果有語句遭到失敗,那麼表將被標記為不可訪問,而且必須被刪除。這一點將妨礙生產系統中進行NLI的增量批量插入,但是對於空表的初始填充比較有用。

在使用導入的情況下,如果為COMMITCOUNT參數采用一個較小的值,那麼會不利於性能。每次提交 100 行或更多行比每次提交一行在性能上更好。也可以忽略 COMMITCOUNT參數,讓DB2 按適當的頻率進行提交。

為了更好地利用多個 CPU和磁盤,可以並發運行多個導入命令。但是應確保每個導入命令都是在它自己的數據庫連接上運行的,並使用“ALLOW WRITE Access” 子句以避免表被鎖定。為運行並發導入,不需要分割輸入文件(DEL 文件)。每個導入命令可以讀取一個輸入文件的不同片段,因為導入命令允許指定 “SKIPCOUNT m ROWCOUNT n”來讀取輸入文件中的m+1 到 m+n 行。

如果需要將非常多的小型 XML 文件從文件系統轉移到一個 DB2 表中,那麼將它們放在一個專用的、文件系統緩存被禁用的文件系統中有助於提高性能。由於每個文件只被讀取和插入一次,因此緩存完全沒有必要。在AIX 上,用-o cio 選項掛載這種文件系統被證明是有益的。

要獲得更多關於插入性能的指南,請參閱文章 “Tips for improving INSERT performance in DB2 Universal Database”(見參考資料)。

總而言之,傳統的插入和日志記錄性能調優對於XML 插入和導入仍然適用。如果將ALLOW WRITE Access 子句添加到每個導入命令中,則可以運行並行導入會話。

提示5:使用新的快照監視器元素檢查XML性能

無論您是正在調查不同頁寬的優點還是XML性能的其他方面,很可能都要像對於關系數據一樣使用DB2 快照監視器。您將發現,與用於數據和索引的已有的計數器相對應,DB2 9 為XML數據提供了新的緩沖池快照監視器元素。由於關系數據和索引存儲在表空間內不同的存儲對象中,因此它們具有不同的讀和寫計數器。DDB2 9中的pureXML 存儲為XML數據引入了一種新的存儲對象,即 XDA,它也有自己的緩沖池計數器。

下面的例子是快照監視器輸出的一個片段。可以看到用於三種不同存儲對象(即數據、索引和 XDA)各自的快照監視器元素。這樣便於單獨針對 XML 監視和分析緩沖和 I/O 活動,而與關系數據互不影響。與 XML 索引有關的任何活動都被包括在已有的索引計數器中。新的XDA 計數器的解釋與對應的關系計數器的解釋相同。例如,如果XDA 物理讀與 XDA 邏輯讀的比率較低,則表明 XML數據的緩沖池命中率較高,這正符合期望。要了解關於緩沖池快照監視器元素的更多詳細信息,請參閱 DB2 文檔。

清單 2.數據、索引和 XDA 存儲對象的監視器輸出

Buffer pool data logical reads       = 221759
Buffer pool data physical reads      = 48580
Buffer pool temporary data logical reads  = 10730
Buffer pool temporary data physical reads = 0
Buffer pool data writes          = 6
Asynchronous pool data page reads     = 0
Asynchronous pool data page writes     = 6
Buffer pool index logical reads      = 8340915
Buffer pool index physical reads      = 54517
Buffer pool temporary index logical reads = 0
Buffer pool temporary index physical reads = 0
Buffer pool index writes          = 0
Asynchronous pool index page reads     = 0
Asynchronous pool index page writes    = 0
Buffer pool xda logical reads       = 2533633
Buffer pool xda physical reads       = 189056
Buffer pool temporary xda logical reads  = 374243
Buffer pool temporary xda physical reads  = 0
Buffer pool xda writes
          = 0
Asynchronous pool xda page reads      = 97728
Asynchronous pool xda page writes     = 0
Asynchronous data read requests      = 0
Asynchronous index read requests      = 0
Asynchronous xda read requests       = 83528

總而言之, 快照監視器中新的XDA 計數器反映了 XML 活動。它們對於了解 XML數據的緩沖池、I/O和臨時空間的使用情況很有用。

提示6:了解 XML 模式驗證的開銷

XML 模式可以定義一組 XML 文檔所允許的結構、元素和屬性、它們的數據類型、取值范圍等。DB2 允許(可選地)根據 XML 模式驗證 XML 文檔。如果選擇驗證文檔,那麼通常是在插入時進行驗證。這是為了達到兩個目的。首先,通過驗證可以確保插入到數據庫中的數據符合模式定義,也就是說可以防止將“垃圾數據(junk data)” 輸入到表中。其次,模式驗證將來自模式的類型注釋添加到每個 XML 元素和屬性,這些類型在DB2 XML 存儲中得到持久化。例如,如果一個 XML 模式定義 dept 表中的雇員 ID(如提示1 所示)為整數,並根據該模式對文檔進行驗證,那麼 DB2 會記得,在每個文檔中雇員 ID 具有類型 xs:integer。在查詢運行時,嘗試在雇員 ID 上執行字符串比較將遭到失敗,並產生一個類型錯誤。

在XML解析期間,XML 模式驗證是一個可選的活動。性能研究表明,如果啟用模式驗證,那麼 XML解析通常要更密集地使用CPU。根據 XML 文檔的結構和大小,尤其是根據使用的XML 模式的大小和復雜性的不同,這一開銷相差很大。例如,您可以發現,由於采用中等復雜程度的模式進行模式驗證,對 CPU的消耗增加了 50%。除非 XML 插入中 I/O 占極大一部分,否則增加的CPU 消耗通常會導致插入的吞吐量下降。

判斷應用程序是否需要更嚴格地對 XML 查詢進行類型檢查,以及檢查XML 模式的遵從性。例如,如果在將XML 文檔存儲到數據庫之前,使用一個應用服務器來接收、驗證和處理 XML 文檔,那麼可能不需要在DB2中對文檔進行驗證。此時,您已經知道它們是有效的。在這種情況下,應避免進行模式驗證,以提高插入性能。但是,如果DB2數據庫從不信任的地方接收XML數據,而又需要在DB2 上確保模式遵從性,那麼就需要在這方面花費一些額外的CPU 周期。

總而言之,為提高插入性能,如果沒有必要,應避免在DB2中執行模式驗證。

提示7:在XPath 表達式中,盡可能使用全限定路徑

假設有一個包含 XML 列的表

create table customer(info XML);

要管理具有以下結構的“customerinfo” 文檔:

清單 3. 示例 XML 文檔

<customerinfo Cid="1004">
  <name>Matt Foreman</name>
  <addr country="Canada">
     <street>1596 Baseline</street>
     <city>Toronto</city>
     <state>Ontario</state>
     <pcode>M3Z-5H9</pcode>
  </addr>
  <phone type="work">905-555-4789</phone>
  <phone type="home">416-555-3376</phone>
</customerinfo>

如果要檢索客戶的電話號碼或他們所居住的城市,無論使用XQuery 還是SQL/XML,都有多種可能的路徑表達式可獲得該數據。通過 /customerinfo/phone和 //phone 都可以獲得電話號碼。同樣,/customerinfo/addr/city和 /customerinfo/*/city 都返回城市。為了得到最佳的性能,使用全限定路徑比使用*或// 更可取,因為使用全限定路徑可以使 DB2 直接導航到所需的元素,而忽略文檔中不相關的部分。

換句話說,如果您知道所需的元素位於文檔中的什麼位置,那麼以全限定路徑的形式提供位置信息會比較有幫助。如果請求 //phone 而不是/customerinfo/phone,那麼就是在請求文檔中任何地方的phone 元素。這需要 DB2 向下導航到文檔的"addr" 子樹中,在文檔的任何級別上查找 phone 元素,而這本是可以避免的開銷。

注意,*和 // 還可能導致不需要的或期望之外的查詢結果。例如,如果有些 “customerinfo” 文檔還包含 “assistant” 信息,就像下面的文檔一樣。那麼路徑 //phone 將返回客戶的電話號碼和助手的電話號碼,而沒有將它們區分開。從查詢結果中無法知道是客戶的電話號碼還是助手的電話號碼,甚至會把助手的電話號碼當作客戶的電話號碼來處理。

清單 4. 文檔中多個層次中的phone和 name 元素

<customerinfo Cid="1004">
  <name>Matt Foreman</name>
  <addr country="Canada">
     <street>1596 Baseline</street>
     <city>Toronto</city>
     <state>Ontario</state>
     <pcode>M3Z-5H9</pcode>
  </addr>
  <phone type="work">905-555-4789</phone>
  <phone type="home">416-555-3376</phone>
  <assistant>
     <name>Peter Smith</name>
     <phone type="home">416-555-3426</phone>
   </assistant>
</customerinfo>

總而言之,在路徑表達式中避免使用*和 //,盡量使用全限定路徑。

提示8:定義傾斜的XML 索引,並避免為任何東西都建索引

假設我們的查詢常常根據客戶姓名搜索 “customerinfo” 文檔。客戶姓名元素上的索引可以大大提高那些查詢的性能。讓我們來看看下面的例子:

清單 5. 利用索引為根據客戶姓名搜索文檔提供支持

create table customer(info XML);
create index custname1 on customer(info)
generate key using XMLpattern '/customerinfo/name' as sql varchar(20);
create index custname2 on customer(info)
generate key using XMLpattern '//name' as sql varchar(20);
select * from customer
where XMLexists('$i/customerinfo[name = "Matt Foreman"]' passing info as $i);

上面定義的兩個索引都適合用於客戶姓名上的XMLEXISTS 謂詞的計算。但是實際上,索引 custname2 比索引 custname1 更大一些,因為它不僅包含客戶姓名的索引條目,而且包括助手姓名的索引條目。只是因為XML 模式 //name 與文檔中任何地方的name 元素相匹配。但是,如果我們永遠不需要根據助手姓名來進行搜索,那麼就不需要為它們編索引。

對於讀操作,索引 custname1 更小一些,因此可能帶來更好的性能。對於插入、更新和刪除操作,索引 custname1 只會引起用於客戶姓名的維護開銷,而索引 custname2 則需要用於客戶和助手姓名的索引維護。如果想得到最佳的插入/更新/刪除性能,並且不需要根據助手姓名進行基於索引的訪問,那麼當然不想花費額外的代價。

另外,請考慮下面的heavyIndex 索引,它 “為任何東西編索引”。它包含每個文本節點(即 XML 列中的每個 XML 文檔中的每個葉子元素值)的索引條目。在插入/更新/刪除操作期間,那樣的索引維護起來非常消耗成本,因而通常不推薦使用這樣的索引。惟一的例外是,具有較少寫活動和不可預測的查詢工作負載的應用程序,這種應用程序難於定義更明確的索引。

create index heavyIndex on customer(info)
generate key using XMLpattern '//text()' as sql varchar(20);

總而言之,在定義 XML 索引時,應該盡可能精確一點,盡量避免使用*和 //。

提示9:將文檔過濾謂詞放入XMLEXISTS中,而不是放入XMLQUERY中

讓我們來考慮下面的表和數據:

create table customer(info XML);

表 2. customer 表中的三行數據

<customerinfo>
  <name>Matt Foreman</name>
  <phone>905-555-4789</phone>
</customerinfo>
<customerinfo>
  <name>Peter Jones</name>
  <phone>905-123-9065</phone>
</customerinfo>
<customerinfo>
  <name>Mary Poppins</name>
  <phone>905-890-0763</phone>
</customerinfo>

對於這個表,假設您想返回電話號碼為“905-555-4789”的客戶的姓名。 您可能禁不住想編寫下面這樣的查詢:

select XMLquery('$i/customerinfo[phone = "905-555-4789"]/name' passing info as "i")
from customer;

但是,這個查詢並不是您想要的,原因有好幾個:

它返回下面這樣的結果集,其中的行數與表中的行數一樣多。這是因為SQL語句沒有 where 子句,因此不能排除任何行。

<name>Matt Foreman</name>

3 record(s) selected

對於表中與謂詞不匹配的每一行,返回一個包含空的XML 序列的行。這是因為XMLQUERY函數中的XQuery 表達式每次應用於一行(文檔),並不會從結果集中去掉一行,只是修改它的值。那個 XQuery 產生的值,當謂詞為true 時為客戶的name 元素,否則為空的序列。這些空行在語義上是正確的(根據 SQL/XML 標准),如果按這種方式編寫查詢,則必須返回它們。

該查詢的性能並不好。首先,不能使用/customerinfo/phone 上的索引,因為查詢不允許排除行。其次,返回很多空行使查詢速度不必要地慢了下來。

為了解決性能問題並 得到所需的輸出,應該在select 子句中使用XMLQUERY函數,只提取客戶姓名,將應該排除行的搜索條件轉移到 where 子句的XMLEXISTS 謂詞中。這將允許使用索引和對行進行過濾,還可以避免空結果行帶來的開銷。像下面這樣編寫查詢:

select XMLquery('$i/customerinfo/name' passing info as "i")
from customer
where XMLexists('$i/customerinfo[phone = "905-555-4789"]' passing info as "i")

<name>Matt Foreman</name>

1 record(s) selected

總而言之,XMLQUERY函數中的謂詞只應用於每個 XML 值當中,所以它們不會排除任何行。文檔過濾和行過濾謂詞應該放入到 XMLEXISTS函數中。

提示10:使用方括號 [ ]來避免 XMLEXISTS中的Boolean 謂詞

一種常見的錯誤是在XMLEXISTS函數中不使用方括號來編寫前面的查詢:

select XMLquery('$i/customerinfo/name' passing info as "i")
from customer
where XMLexists('$i/customerinfo/phone = "905-555-4789"' passing info as "i")

這將產生以下結果:

<name>Matt Foreman</name> <name>Peter Jones</name> <name>Mary Poppins</name>

3 record(s) selected

XMLEXISTS 謂詞中的表達式使 XMLEXISTS 總是為true。因此,沒有行被排除。這是因為,對於一個給定的行,只有當裡面的XQuery 表達式返回空序列時,XMLEXISTS 謂詞才為false。然而,如果不使用方括號,XQuery 表達式就是一個總是返回 Boolean 值的Boolean 表達式,而不會返回空序列。注意,XMLEXISTS 只是檢查一個值的存在,如果存在一個值,即使這個值碰巧為Boolean 值 “false”,XMLEXISTS 也將返回 true。雖然這並不是您想實現的效果,但是根據 SQL/XML 標准,這的確是正確的行為。

同樣,其影響是不能使用phone 上的索引,因為沒有行被排除,所以會收到大量不需要的行。而且,在使用兩個或更多謂詞時,不要犯同樣的錯誤,例如在下面這個查詢中:

清單 6. XMLEXISTS中兩個謂詞的不恰當的使用

select XMLquery('$i/customerinfo/name' passing info as "i")
from customer
where XMLexists('$i/customerinfo[phone = "905-555-4789"] and
     $i/customerinfo[name = "Matt Foreman"]'
   passing info as "i")

這個查詢使用了方括號,那麼它錯在哪裡呢?XQuery 表達式仍然是一個 Boolean 表達式,因為它的形式是“exp1 and exp2”。下面是編寫這個查詢的正確方式,這樣編寫查詢可以過濾行,並允許使用索引:

清單 7. 可以過濾行並允許使用索引的正確查詢

select XMLquery('$i/customerinfo/name' passing info as "i")
from customer
where XMLexists('$i/customerinfo[phone = "905-555-4789" and name = "Matt Foreman"]'
   passing info as "i")

總而言之,在XMLEXISTS中不要使用Boolean 謂詞。將謂詞放在方括號中,包括任何 “and”和 “or”。

提示11:使用RUNSTATS 收集 XML數據和索引的統計信息

RUNSTATS 實用程序已經被擴展,現在可以收集關於XML數據和 XML 索引的統計信息。DB2 基於成本的優化器使用這些統計信息為XQuery和 SQL/XML 查詢生成有效的執行計劃。因此,像對待關系數據那樣,繼續使用RUNSTATS。如果表包含關系數據和 XML數據,而您又只想刷新關系數據的統計信息,那麼可以帶新的子句 “EXCLUDING XML COLUMNS”來執行RUNSTATS。如果沒有這個子句,缺省的也是可取的行為是總是收集關系數據和 XML數據的統計信息。

對於關系數據和 XML數據,可以啟用抽樣(sampling)來減少執行runstats的時間。在一個大型的數據集上,10%的數據(或者更少)的統計信息通常仍然非常具有代表性。無論選擇多大的抽樣百分比,runstats 允許對行(Bernoulli sampling)或頁面(system sampling)進行抽樣。行級的抽樣讀取所有數據頁,但是只考慮每頁上一定百分比的行。而頁級的抽樣則可以顯著減少 I/O,因為它只讀取一定百分比的數據頁。因此,如果表中不僅包含 XML數據,而且還包含相當數量的關系數據,頁抽樣可以顯著提高性能。但是,如果關系數據值是高度聚集的,那麼行級抽樣可以產生更精確的統計信息。

下面是一些例子。第一個 runstats 命令為表 customer和它的所有索引收集最全面、最詳細的統計信息,而沒有采用抽樣。如果執行時間允許的話,這樣做是理想的。第二個命令收集同樣的統計信息,但是只收集 10%的頁面的統計信息。在很多情況下,這樣做可以為優化器提供接近於第一個命令的精確性的統計信息,但是可以更快地返回結果。第三個命令抽取 15%的行,但是不收集分布統計信息,並對索引也應用了抽樣,這不同於第一個命令和第二個命令。

清單 8. 使用RUNSTATS 收集統計信息

runstats on table myschema.customer
with distribution on all columns and detailed indexes all;
runstats on table myschema.customer
with distribution on all columns and detailed indexes all tablesample system (10);
runstats on table myschema.customer
on all columns and sample detailed indexes all tablesample bernoulli (15);

總而言之,如果有可用的XML 統計信息,DB2 可以生成更好的執行計劃。像通常那樣使用runstats,或者利用抽樣使用runstats,以減少它的執行時間。

提示12:如何使用SQL/XML 發布視圖將關系數據暴露為XML

SQL/XML 發布函數允許將關系數據轉換成 XML 格式。較好的做法是將SQL/XML 發布函數藏在一個視圖定義中,使應用程序和其他查詢可以從視圖中選擇構造好的XML 文檔,而不必與發布函數本身打交道。

清單 9. 隱藏在視圖中的SQL/XML 發布函數

create table unit( unitID char(8), name char(20), manager varchar(20));
create view UnitVIEw(unitID, name, unitdoc) as
  select unitID, name,
     XMLELEMENT(NAME "Unit",
       XMLELEMENT(NAME "ID", u,unitID),
       XMLELEMENT(NAME "UnitName", u.name),
       XMLELEMENT(NAME "Mgr", u.manager)
         )
  from unit u;

注意,我們在視圖定義中包括了一些關系列。這並沒有產生任何物理上的冗余,因為它只是一個視圖,而不是物化的視圖。暴露關系列有助於有效地查詢這個視圖。假設我們需要取一個對應於特定單位的XML 文檔。下面三個查詢都可以實現這一點,但是第三個查詢要好於前兩個查詢。

在前兩個查詢中,過濾謂詞是在構造的XML 上表達的。但是,XML 謂詞不能應用於底層關系列或它的索引。因此,這些查詢要求視圖為所有單位構造 XML,然後選出對應於單位 “WWPR”的XML。這並非最佳方法。

以下查詢可能取得次優性能:

清單 10. 次優的查詢

select unitdoc
from UnitVIEw
where XMLexists('$i/Unit[ID = "WWPR"]' passing unitdoc as "i");
for $u in db2-fn:XMLcolumn('UNITVIEW.UNITDOC')/UNIT
where $u/ID = "WWPR"
return $u;

第三種方法使用一個關系謂詞來確保只為“WWPR” 構造 XML 文檔,從而減少了運行時間,對於大型的數據集,這種方法帶來的性能好處尤其顯著。這個查詢可以取得很好的性能:

清單 11.性能較好的查詢

select unitdoc
from UnitVIEw
where UnitID = "WWPR";

總而言之,將關系列包括在SQL/XML 發布視圖中,在查詢視圖時,在那些關系列上表達謂詞,而不是在構造的XML 上表達謂詞。

提示13:如何使用XMLTABLE 視圖以關系格式暴露 XML數據

創建一個視圖以 XML 格式暴露關系數據,這樣做很有用。同樣,您可能也想使用一個視圖將XML數據暴露為關系格式。提示12中的提醒也適用於這裡的情況,只不過順序要倒過來。讓我們來看看下面的例子,其中使用了 SQL/XML函數 XMLTABLE 以標量格式返回 XML 文檔中的值:

清單 12. 以標量格式返回的XML 文檔中的值

create table customer(info XML);
create view myvIEw(CustomerID, Name, Zip, Info) as
SELECT T.*, info
FROM customer, XMLTABLE ('$c/customerinfo' passing info as "c"
  COLUMNs
  "CID"   INTEGER   PATH './@Cid',
  "Name"  VARCHAR(30) PATH './name',
  "Zip"   CHAR(12)   PATH './addr/pcode' ) as T;

注意,我們將XML 列 info 包括在視圖定義中,以便幫助有效地查詢這個視圖。假設您想基於給定的ZIP 號碼檢索客戶 ID和姓名的一個標量列表。下面兩個查詢都可以實現這一點,但是第二個查詢比第一個查詢的性能要好。在第一個查詢中,過濾謂詞是在由 XMLTABLE函數生成的CHAR 列 “Zip” 上表達的。但是,關系謂詞不能應用於底層的XML 列或它的索引。因此,這個查詢要求視圖為所有 客戶生成行,然後根據郵政編碼 “95141” 選擇符合條件的行。這不是最佳方法。第二個查詢使用一個 XML 謂詞來確保只生成對應於“95141”的行,從而減少了運行時間,對於大型的數據集,這種方法帶來的性能好處尤其顯著。

清單 13. 包含 XML 謂詞的查詢

-- may perform suboptimal:
select CustomerID, Name
from myvIEw
where Zip = "95141";
-- will perform well:
select CustomerID, Name
from myVIEw
where XMLexists('$i/customerinfo[addr/pcode = "95141"]' passing info as "i");

如果定義視圖所用的基表不僅包含一個 XML 列,還包含有索引的關系列,那麼應該將那些關系列包含在視圖定義中。如果對視圖的查詢包含關系列上的具有高度限制性的謂詞,那麼 DB2 使用關系索引來過濾符合條件的行,得到較小數量的行,然後在返回最終結果集之前,對這個中間結果應用XMLTABLE和其他謂詞。

總而言之,在使用XMLTABLE 視圖將XML數據暴露為關系格式時要加以小心。應該盡可能在視圖定義中包含其他的列,以便在那些列上表達過濾謂詞,而不是在XMLTABLE 列上表達過濾謂詞。

提示14:對於短小的查詢或 OLTP應用程序,使用帶參數占位符的SQL/XML語句

非常短的數據庫查詢常常執行得很快,以至於編譯和優化它們的時間占總體響應時間的很大比例。因此,只將它們編譯(“准備”)一次,然後在每次執行時只傳遞謂詞的字面值,這樣做很有用。雖然 DB2 9 XQuery 不支持外部參數,但是SQL/XML函數 XMLQUERY、XMLTABLE和 XMLEXISTS 卻支持外部參數。這些函數允許傳遞 SQL參數占位符,作為嵌入式 XQuery 表達式的一個變量。對於具有短小、重復的查詢的應用程序,建議這樣做。

清單 14. 硬編碼的字面值

for $c in db2-fn:XMLcolumn('CUSTOMER.INFO')/customer
where $c/phone = "905-555-4789"
return $c;
select info
from customer
where XMLexists('$i/customerinfo[phone = "905-555-4789"]'
        passing info as "i")

清單 15. 使用參數占位符

select info
from customer
where XMLexists('$i/customerinfo[phone = $p]'
        passing info as "i", cast(? as varchar(12)) as "p")

總而言之,短小的查詢和 OLTP 事務就像含參數占位符的預置語句一樣,具有更快的速度。對於XML,這要求 SQL/XML 將SQL 型參數傳遞給 XQuery 表達式。

提示15:避免 XML 插入和檢索期間出現代碼頁轉換

XML 不同於DB2中其他類型的數據,因為它可以在內部編碼,也可以在外部編碼。內部編碼意味著XML數據的編碼可以源於數據本身。外部編碼意味著編碼源於外部信息。用於與 DB2 交換 XML數據的應用程序變量的數據類型決定了如何得到編碼。如果應用程序為XML 使用字符類型的變量,那麼它就要在外部編碼,即在應用程序代碼頁中編碼。如果使用二進制應用程序數據類型,那麼 XML數據考慮采用內部編碼。內部編碼意味著編碼是由 XML 文檔本身包含的Unicode Byte-Order mark(BOM)或編碼聲明決定的,例如

<?XML version="1.0" encoding="UTF-8" ?>

從性能的角度來看,我們的目標是盡可能避免代碼頁轉換,因為它們要消耗額外的CPU 周期。內部編碼的XML數據比外部編碼的數據更為可取,因為它可以防止不必要的代碼頁轉換。這意味著,在應用程序中,應該優先選擇二進制數據類型,而不是字符類型。例如,在CLI中,當使用SQLBindParameter() 將參數占位符綁定到輸入數據緩沖區時,應該使用SQL_C_BINARY數據緩沖區,而不是SQL_C_CHAR、SQL_C_DBCHAR或SQL_C_WCHAR數據緩沖區。當從Java應用程序中插入XML數據時,將XML數據讀入為二進制流(setBinaryStream)比字符串(setString)更好。類似地,當Java應用程序從DB2接收XML,並將它寫到一個文件的時候,如果XML被寫成非二進制數據,就會發生代碼頁轉換。

當從DB2中將XML數據檢索到應用程序中時,該數據被串行化。串行化是XML解析的逆向操作。這個過程將DB2的內部 XML 格式(解析後的樹型表示)轉換成應用程序能理解的原始的XML 格式。在大多數情況下,最好是讓DB2執行隱式的串行化。這意味著SQL/XML語句只需選擇XML類型的值,而 DB2 盡可能高效地將其串行化為應用程序變量,如下面的例子所示:

清單 16. 含隱式串行化的查詢

create table customer(info XML);
select info from customer where...;
select XMLquery('$i/customerinfo/name' passing info as "i")
from customer
where...;

如果應用程序要處理非常大的XML 文檔,那麼可以將LOB 定位符用於數據檢索。這就要求顯式地串行化為LOB類型,更可取的是BLOB類型,因為串行化為字符類型(例如CLOB)會帶來編碼問題和不必要的代碼頁轉換。顯式串行化使用XMLSERIALIZE函數:

select XMLSERIALIZE(info as BLOB(1M)) from customer where...;

總而言之,在應用程序中應該使用二進制數據類型來與 DB2 交換 XML,因為這樣做可以避免不必要的代碼頁轉換。要了解編碼問題,如果有疑問,可以參考 DB2 9 文檔中的詳細指南。

結束語

為了在DB2中達到最佳 XML性能,首先要從使用DB2的自治特性(例如自治存儲和自調優內存管理)開始。這樣可以為很多應用程序提供很好的、開箱即用的性能。這樣做還為DBA 節省了寶貴的時間,讓他們可以將精力放在更有針對性的性能調優上。所有傳統的DB2性能方面的知識仍然適用於XML,下面列出的各種developerWorks 文章都對它們做了論述。

在此基礎上,本文中的15 個提示可以在一些常見的與 XML 有關的性能方面為您提供幫助。如果需要改善 XML應用程序的性能,不必應用所有這 15 個提示,其中往往只有一兩個提示是與您的情況相關的。例如,如果由於表空間配置有問題,系統在I/O 方面存在瓶頸,那麼減少不必要的代碼頁轉換仍然無濟於事。類似地,如果實際上是需要執行runstats來支持更好的查詢執行計劃,那麼使用SQL/XML參數占位符可能沒有幫助。簡而言之,本文中的提示可以幫助您避免性能問題,但是要修復觀察到的性能問題,首先需要找出問題的根源和瓶頸。和對待關系數據一樣,可以使用DB2中的標准診斷工具(例如visual explain、db2exfmt和快照監視器)來調查XML性能。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved