DB2 での pureXML パフォーマンスのための 15 のベスト・プラクティス

DB2 9 では、pureXML サポートが導入されています。これはつまり、XML データが固有の階層フォーマットで保管され、クエリーされることを意味しています。DB2 では、XML データをクエリーするために SQL/XML と XQuery という 2 つの言語を提供しています。また、DB2 9 は高度な XML 索引機能を持ち、XML Schema 検証をサポートしています。DB2 のパフォーマンスに関する従来からのガイドラインの大部分は XML データにも適用できますが、この記事では特に XML に固有のパフォーマンスについてのヒントを説明します。この記事は、DB2 9.5 用に更新されています。[2009年5月26日: リスト 12 およびリスト 13 のコードを修正しました (編集者より)。]

Matthias Nicola, DB2/XML Performance, IBM Silicon Valley Laboratory

Author photo: Matthias NicolaNicola 博士は、IBM Silicon Valley Lab の XML データベース・パフォーマンスの技術リーダーです。彼の業務は、XQuery、SQL/XML、また DB2 ネイティブの XML 機能を含め、DB2 での XML パフォーマンスのあらゆる側面を対象としています。Nicola 博士は DB2 XML 開発チームや XML を使用する顧客やビジネス・パートナーと緊密に業務を進めながら、XML ソリューションの設計や実装、最適化に協力しています。IBM に入社する前には、Informix Software でデータ・ウェアハウスのパフォーマンスに関する業務を行っていました。また、分散データベースや複製データベースに関する研究と業界プロジェクトにも 4 年間携わっていました。彼は、1999年にドイツの Technical University of Aachen でコンピューター・サイエンスの博士号を取得しています。



2009年 5月 26日 (初版 2006年 10月 05日)

はじめに

DB2 9 での pureXML サポートによって、XML データを効率的に、多様な方法で管理することができます。多くの XML アプリケーションにとって、パフォーマンスは大きな関心事です。そして DBA やアプリケーション設計者は、適切なパフォーマンスを実現する責任があります。まず、従来からの DB2 パフォーマンス・ガイドラインには、あらゆるものが網羅されています。例えば、CPU やメモリー、ディスクなどの構成のバランス、表スペース、バッファー・プール調整、ロック、ロギング、クエリー実行計画、等々です。こうした問題は DB2 に関するこれまでの記事 (「参考文献」を参照) で解説されていますが、DB2 で XML を扱う場合にも相変わらず重要です。

幸い、こうした問題のいくつかは、DB2 のオートノミック機能 (自動保管や、自己調整によるメモリー管理など) によって処理されます。そうした機能は、多くのアプリケーションに対して高レベルのパフォーマンスを発揮し、人の介在をほとんど必要としません。しかし、パフォーマンス要求の厳しい XML アプリケーションでは、さらに考慮が必要なことがあります。この記事は、そうした状況に焦点を当て、DB2 9 で XML 関連のアプリケーションから最高のパフォーマンスを引き出すためのヒントとガイドラインを示すことにします。

以下は、この記事で説明する、XML パフォーマンスを高めるための 15 のヒントです (順不同です)。この 15 のヒントはさまざまな領域に関するものですが、これまでの経験から、こうしたヒントの中の 1 つか 2 つをパフォーマンスに問題があるアプリケーションに適用するだけで、目標とするパフォーマンスを実現できることがよくあります。

こうしたパフォーマンスのヒントに関する説明では、DB2 の基本的な管理とパフォーマンスに関連する作業、また DB2 の pureXML サポートなどに慣れていることを前提としています。例えば、XML 列や XML 索引、また SQL/XML や XQuery を使って XML データをクエリーする方法などを知っている必要があります。前提条件となるこうした話題に関しては、これまでに公開された developerWorks の記事の中で説明されています (「参考文献」を参照してください)。


DB2 の XML パフォーマンスのためのヒント

ヒント 1: XML 文書の粒度の選択に注意する

XML アプリケーションを設計する際、特に XML 文書の構造を設計する際には、1 つの XML 文書の中に、どのようなビジネス・データをまとめるべきかを定義しなければなりません。例えば、下記の部門表 (department table) では、部門ごとに 1 つの XML 文書を使います (中粒度)。アプリケーションが、部門を主な粒度としてデータにアクセスし、処理するのであれば、これは妥当な選択です。別の方法として、複数部門、あるいは多くの部門 (例えば 1 つの組織単位に属する部門など) を 1 つの XML 文書にまとめることもできます (粗粒度)。しかし、通常は 1 度に 1 つの部門しか処理しないのだとすると、そうした選択は最適ではありません。

表 1. create table dept( unitID char(8), deptdoc xml)
unitIDdeptdoc
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...
......

また、所属部門を示す「dept」属性を各従業員に追加で持たせ、従業員ごとに 1 つの XML 文書を作ることもできます (細粒度)。もし従業員自身が、自分が関心を持つビジネス・オブジェクトを使用し、そのオブジェクトに対して同じ部門の別の従業員も独立にアクセスし、処理することが多いのであれば、これは非常に適切な選択です。しかし、アプリケーションが 1 つの部門の全従業員をまとめて処理することが多いのであれば、部門ごとに 1 つの XML 文書であった方が適切です。

特に、多くの独立したビジネス・オブジェクトを 1 つの文書の中でバッチ処理することは奨められません。DB2 は XML データに索引を付け、文書ごとのレベルでフィルタリングします。従って、XML 文書の粒度が細かいほど、索引によるアクセスで有利になる可能性が高いと言えます。また、アプリケーションが DOM パーサーを使って DB2 からの XML を取り込む場合にも、小さな文書の方がパフォーマンスが高くなります。

XML 文書の設計に関連する質問として、属性を使うべきなのか要素を使うべきなのか、そしてその選択がパフォーマンスにどう影響するのか、という質問がよく出されます。これは、パフォーマンスに関する質問と言うよりも、むしろデータ・モデリングに関する質問です。従ってこの質問は、XML 以前の SGML と同じくらい古く、熱い議論が繰り返さていますが、誰もが同意するような一般的な答えは得られていません。しかし、XML 要素は繰り返すことができ、ネストできるため、属性よりも柔軟である、という重要な事実に注目する必要があります。例えば、この部門文書の場合では、「phone」という要素を使っています。これによって、ある従業員が複数の番号を持っている場合には、「phone」のオカレンスを複数持つことができます。また、電話番号をいくつかの部分に分割する必要が出てきた場合にも、拡張が可能です。つまり「phone」要素は、国コードや市外局番、内線番号などの子要素を持てるのです。もし「phone」が従業員要素の属性だったとすると、1 人の従業員に対して 1 つしか存在できず、子要素を追加することもできません。そのため、時間と共にスキーマを進化させる上での障害となります。すべてのデータを、まったく属性を持たないようにモデル化することもできます。しかし (要素ごとの) 繰り返しがなく、サブフィールドも持たないことが事前にわかっているデータ項目に対しては、属性は非常に直感的な選択です。要素は開始タグと終了タグを持ちますが、属性は 1 つのタグしか持たないのため、属性を使うと XML が少し短くなります。DB2 では、クエリーや述部、索引定義などに、要素と同じくらい容易に属性を使うことができます。属性は要素ほど拡張性が高くないため、DB2 では保管やアクセスの最適化を適用できるようになっています。これは、特にデータ・モデリングの面から実際には要素が求められている場合には、属性を要素に変換するための推奨策ではなく、パフォーマンスに関する追加的な特典と考えるべきものです。

要約: XML 文書の粒度の選択には、予測される支配的なアクセス粒度を考慮する必要があります。よくわからない場合には、一般的に言って、より粒度を細かく、より小さな XML 文書にした方が適切です。

ヒント 2: XML パフォーマンスを高めるためには、DMS と、大きなページを使う

DMS (database managed table spaces) は、SMS (operating system managed table spaces) よりも高いパフォーマンスを実現します。これはリレーショナル・データの場合ですが、XML の読み書きアクセスでは、それがさらに顕著です。DB2 9 では、新たに作成される表スペースは、デフォルトで DMS です。また、人が介在しなくても DMS コンテナーが必要に応じて成長するように、自動保管を備えた DMS 表スペースを使うようにお奨めします。もしXML 文書が大きすぎ、表スペースの 1 つのページに収まらない場合には、DB2 はその文書を複数の領域に分割して複数のページに保管します。これはアプリケーションにとっては透過的であり、DB2 は、組み合わせて 1 つの文書にできる限界である、2GB までの XML 文書を処理することができます。

一般的に、1 つの文書当たりの領域 (分割) の数が少なければ少ないほど、パフォーマンスは高くなります。特に、挿入や文書全体を取り出す場合は、それが顕著です。ある文書が 1 つのページに収まらない場合、その文書をいくつに分割するかは、ページ・サイズ (4KB、8KB、16KB、または 32KB) に依存します。表スペースのページ・サイズが大きいほど、文書当たりの分割数は少なくなります。例えば、ある文書が 4KB ページ構成の 40 ページに分割されたとしましょう。そうすると、その同じ文書は、8KB のページであれば 20 ページ、16KB のページであれば 10 ページ、32KB のページであれば 5 ページに保管できてしまいます。もし選択されたページ・サイズよりも XML 文書が大幅に小さければ、1 つのページに複数の小さな文書が保管できるため、スペースに無駄がなくなります。

一般的なルールとして、XML データのページ・サイズは、32KB を最大として、想定される平均的な文書サイズの 2 倍よりも小さくならないように選択します。リレーショナル・データと XML データのページ・サイズを 1 つにする場合、あるいはデータと索引のページ・サイズを 1 つにする場合、32KB というページ・サイズは XML データには有益ですが、リレーショナル・データや索引アクセスには少し弊害があります。そうした場合には、16KB または 8KB ページの方が、どちらにとってもより良い選択かもしれません。

ヒント 3: XML の保管オプションを活用する - インライン化、圧縮、個別表スペース

XML データの保管オプションについて検討するために、以下のサンプルの表について考えてみましょう。この表にはリレーショナル・データと XML データが含まれています。

リスト 1. リレーショナル・データと XML データを持つサンプルの表
create table product(pid bigint, name varchar(20), brand varchar(35), 
                     category integer, price decimal, description XML);

この表の定義では、デフォルトでリレーショナル・データと XML データは同じ表スペースに保管されるようになっています。これはつまり、リレーショナル・データと XML データは同じページ・サイズを使用し、同じバッファー・プールにバッファーされるということです。また、表スペース内では、リレーショナル・データは DAT オブジェクト内に保管され、XML データは XDA オブジェクト内に配置されます。これは XML 文書は LOB と同様で、表のデータ・ページの 1 行に収めるには大きすぎるためです。たいていのアプリケーション・シナリオでは、このデフォルトの配置のおかげで適切なパフォーマンスを実現できています。

パフォーマンス分析を行った結果、XML データには大きなページ・サイズが、リレーショナル・データや索引には小さなページ・サイズが必要だとわかった場合には、別々の表スペースを使うことができます。テーブルを定義する際に、「長い」データを、別のページ・サイズを持つ別の表スペースに持っていくのです。長いデータには、LOB と XML データが含まれます。

次の例は、それぞれ 4KB と 32KB のページを持つ、2 つのバッファー・プールと 2 つの表スペースを定義しています。(表スペースは、必ず同じページ・サイズを持つバッファー・プールを要求することに注意してください。) 表「product」は、4KB のページを持つ表スペース「relData」に割り当てられます。この表のすべての列は、XML 列「description」を除いて、この表スペースに保管されます。そしてこの XML 列は、表スペース「xmldata」の 32KB ページに保管されます。

リスト 2. 別々の表スペースとバッファー・プールに格納されるリレーショナル・データと XML データ
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 の表スペースのデフォルトは DB2 V8 のときとは異なっています。明示的に指定されない限り、新しい表スペースは、大きな行 ID を持つ DMS として作成されます。これは、4KB のページを持つ表スペースであれば、V8 での 64GB ではなく 2TB にまで、そして 32KB のページを持つ表スペースであれば、512GB ではなく 16TB にまで成長することを意味します。また、1 ページ当たり 255 行という制限はなくなり、32KB のページに 2335 行まで許されます。従って、ページ当たりの行数制限があるためリレーショナル・データに小さなページを使う、ということはなくなります。

DB2 9.5 はまた、XML データを「インライン化」した状態で圧縮して保管できるようになっています。XML 文書の一部または全体が十分小さく、DAT オブジェクト内の、ベースとなる表ページの行に収まる場合は、リレーショナル行にインライン化することも可能です。これにより、XML データに直接的にアクセスすることができ、リダイレクトによる XDA オブジェクトへのアクセスを回避することができます。XML 列の文書の中にインライン化するには大きすぎる文書がある場合は、従来どおり XDA オブジェクトに「アウトライン化」して保管します。インライン化された文書には索引を付けるための領域が必要ないため、インライン化をすることで索引用の領域を大幅に減らすことが可能になります。They always consist of a single, inlined region. また、インライン化された文書は通常の DB2 行圧縮を使用して圧縮することもできます (リスト 3 )。

リスト 3. インライン化して圧縮した XML ストレージ
create table product(pid bigint, name varchar(20), brand varchar(35), 
                     category integer, price decimal, 
                     description XML inline length 3000) compress yes;

この例では、XML 列に「inline length 3000」というオプションが定義されています。このオプションが意味するのは、3000 バイト以下の文書はインライン化されて保管されるということです。インライン化の際に関係してくるサイズは、ファイル・システム上でのテキストとしての XML 文書のサイズではなく、DB2 上での XML 構文解析後の文書サイズです。インライン化した場合の文書の長さは、ページ・サイズから表の中の他の列のサイズを引いたよりも小さくなければなりません。インライン化された XML データは常に表のリレーショナル列と同じ表スペースに配置され、別のページ・サイズのところに保管されることも、別の表スペースに保管されることもありません。

この例の表では、「compress yes」というオプションが定義されているので、リレーショナル・データとインライン化された XML 文書は圧縮されます。インライン化された XML データを 70 から 85 パーセント圧縮するのは一般的ではありません。以下のステートメントを使うと、「product」表の圧縮の割合を確認することができます。

リスト 4. 圧縮の割合を確認するための管理用関数
select tabname,pages_saved_percent,bytes_saved_percent 
from table(sysproc.admin_get_tab_compress_info('MYSCHEMA','PRODUCT','ESTIMATE')) as t

システムの傾向として CPU バウンドよりも I/O バウンドの方が強い場合は、XML データを圧縮することで著しいパフォーマンスの向上が見られるようになります。しかし注意しなければならないことは、インライン化によりデータ・ページの行のサイズが大幅に増大することです。行のサイズが大きくなると、今度はページ当たりの行数が減ることになります。そうなると、表のリレーショナル列にしかアクセスしないクエリーはインライン化しない場合と比べると、はるかにたくさんのページを読み取る必要がでてきます。その結果、より多くの I/O アクセスが生じることになり、このようなクエリーのパフォーマンスの低下につながってきます。主に使用するクエリーが、常に XML 列にアクセスするようであれば、こうしたことによる影響はありません。

要約: XML データのために別の表スペースを検討する際には、常識を働かせることです。バッファー・プールや表スペースの種類が少なく、ページ・サイズの種類も少なければ、物理的なデータベースの設計が単純になり、管理や維持、調整も容易になります。実際にパフォーマンス上のメリットが明確にわかっている場合を除いて、さまざまなページ・サイズを持ち込むことは避けるべきです。インライン化と圧縮を使ってストレージの使用量を抑え、I/O 動作のパフォーマンスを向上させてください。

ヒント 4: XML データを高速にバルク・インサートするための DB2 の構成方法

DB2 9 は、ファイルシステムから DB2 の表に XML データを移動するためのオプションとして、挿入とインポートという 2 つをサポートしています。インポート・ユーティリティーは、実際には一連の挿入動作を行うため、挿入とインポートはパフォーマンスやチューニングの面での特徴は似ています。

DB2 9 5 以降、XML データを表に移動する際には、DB2 LOAD ユーティリティーを使うこともできるようになっています。LOAD ユーティリティーを使用する主なメリットは、XML データの場合もリレーショナル・データの場合と同じで、パフォーマンス向上のため、データのロギングが行われず、自動的に並列処理が行われるといったことです。DB2 は CPU の数と表スペースの内容に基づき、デフォルトの並列処理の内容を決定しますが、LOAD コマンドの構文で CPU_PARALLELISM と DISK_PARALLELISM の両パラメーターを使用して、CPU と I/O の並列処理の設定をすることも可能です。

そのアプリケーションが (おそらく並行挿入スレッドを使って) バルク・インサートを行うのか、それともインポートまたはロードを使うのかによって、次のパフォーマンス・ガイドラインを適用することができます。

  • 重要な前提条件として、大きなページ・サイズを持つ DMS 表スペースを使います (ヒント 2 を参照してください)。
  • 対象となる表に対して索引を定義しなかったとしても、DB2 の pureXML 保管機構は、効率的な XML 保管アクセスを行うために、いわゆる領域索引やパス索引を透過的に行います。これによって、索引読み取りのサポートに十分なバッファー・プール・スペースが提供されます。
  • 表に対してユーザー定義の XML 索引が複数必要な場合、一般的には、バルク・インサートの後で作成するよりもバルク・インサートの前に定義した方が適切です。挿入の動作中、すべての XML 索引に対する索引エントリーを生成するための処理は、各 XML 文書に対して一度しか行われません。しかし「create index」文を複数回発行すれば、XML 列の中のすべての文書が複数回トラバースされます。
  • 非常に大量の小さな XML ファイルをファイルシステムから DB2 の表に移動する場合、そうしたファイルを、キャッシュ動作を無効にした専用のファイルシステムに置くことでパフォーマンスを高めることができます。各ファイルの読み取りは 1 度しか行われないため、キャッシュ動作は必要ありません。AIX では、このファイルシステムを -o cio オプションを付けてマウントするとよいことがわかっています。

挿入とインポートの操作に関しては、次のガイドラインを検討してみてください。

  • 「ALTER TABLE <テーブル名> APPEND ON」は、表に対する追加モードを有効にします。これによって、新しいデータは既存のページの空いた空間に入れられるのではなく、表の最後に付加されます。これによって、バルク・インサート実行時のパフォーマンスが改善されます。
  • ログ・バッファー・サイズ (LOGBUFSZ) とログ・ファイル・サイズ (LOGFILSIZ) を増やすと、挿入のパフォーマンスを高めることができます。XML の挿入の場合、行当たりのデータ量はリレーショナル・データよりもずっと大きくなりがちなため、これは特に重要です。ログのためには高速の I/O デバイスが推奨されます。
  • ロギングを回避するには、「ALTER TABLE <テーブル名> ACTIVATE NOT LOGGED INITIALLY」(NLI) を使います。しかし、もしこの文が失敗すると、その表はアクセス不能としてマーキングされ、ドロップされてしまうことに注意してください。このため、多くの場合 NLI は実稼働システムでのインクリメンタルなバルク・インサートでは禁止しますが、空の表に初期データを入れる際には便利です。NLI を使うと対象の表に対する挿入とインポートとの並行動作が行われないこと、そして並列処理の方が NLI より高いパフォーマンスが実現できる可能性があるということに注意してください。
  • インポートを使う場合、COMMITCOUNT パラメーターに小さな値を使うと、パフォーマンスの障害になりがちです。100 行、あるいはそれ以上ごとにコミットした方が、1 行ごとにコミットするよりもパフォーマンスは高くなります。また、COMMITCOUNT パラメーターを省略し、DB2 に好きなだけ頻繁にコミットさせることもできます。
  • 複数の CPU やディスクを有効に利用するために、複数のインポート・コマンドを同時に実行することができます。各インポートは、それぞれ独自にデータベースに接続された上で実行される必要があり、また「ALLOW WRITE ACCESS」節を使って表のロックを回避する必要があります。同時インポートを実行するために、別の入力ファイル (DEL ファイル) を用意する必要はありません。インポート・コマンドでは、「SKIPCOUNT m ROWCOUNT n」を指定して入力ファイルの m+1 行から m+n 行までを読むことができるため、各インポートは同じ入力ファイルから別々の部分を読み取ることができるのです。

挿入のパフォーマンスに関する他のガイドラインとしては、記事「Tips for improving INSERT performance in DB2 Universal Database」を参照してください (「参考文献」を参照)。

要約: 挿入やロギングのパフォーマンスに関する従来のチューニングは、XML の挿入やインポートにも有効です。また、各インポート・コマンドに「ALLOW WRITE ACCESS」節を追加すれば、インポート・セッションを並列に実行することもできます。DB 9 5 の場合、インポートの代わりにロードを使うようにします。

ヒント 5: 新しいスナップショット・モニター要素を使って XML パフォーマンスを調べる

さまざまなページ・サイズを使う利点を調べる場合であっても、あるいは XML パフォーマンスの他の側面を調べる場合であっても、リレーショナル・データの場合と同じように DB2 のスナップショット・モニターを使いたいと思う人が多いのではないでしょうか。DB2 9 では、XML データに対するバッファー・プール・スナップショット・モニター要素が新たに提供されています。これは従来からある、リレーショナル・データや索引のカウンターに対応するものです。リレーショナル・データと索引は、表スペース内の別のストレージ・オブジェクトに保管されるため、それぞれ別の読み取りカウンターと書き込みカウンターを持っています。DB2 9 の pureXML ストレージでは、XML データに対して XDA という新しいストレージ・オブジェクトを導入しており、これも独自のバッファー・プール・カウンターを持っています。

下記の例は、スナップショット・モニター出力のスニペットです。データ、索引、そして XDA という 3 つの別々のストレージ・オブジェクトに対して、さまざまなスナップショット・モニター要素があることがわかります。これによって、XML に関するバッファー動作や I/O 動作を、リレーショナル・データとは別にモニターし、分析することができます。XML 索引に関係するすべての動作は、既存の索引カウンターの中に含まれています。新しい XDA カウンターの解釈は、これまでのリレーショナル・カウンターの解釈と同じです。例えば、XDA 論理読み取りに比べて XDA 物理読み取りの比率が低い場合、それは XML データのバッファー・プールをヒットする率が高いことを意味し、望ましいと言えます。バッファー・プール・スナップショット・モニター要素の詳細については、DB2 のドキュメンテーションを参照してください。

リスト 5. データと索引、そして 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 に関する動作を反映しています。XDA カウンターを利用することで、バッファー・プールや I/O、一時スペースなどが、どのように XML データに使われているかを理解することができます。

ヒント 6: XML スキーマ検証のオーバーヘッドに注意する

XML スキーマでは、一連の XML 文書に許される構造や、要素や属性、それらのデータ型、値の範囲などを定義することができます。DB2 では、(オプションとして) XML 文書を XML スキーマに対して検証することができます。文書を検証することを選択した場合、通常は挿入時に検証を行います。これには 2 つの目的があります。第 1 に、検証することによって、データベースに挿入されたデータがスキーマ定義に準拠していることを保証することができます。つまり「ジャンク・データ」が表の中に入るのを防ぐことができます。第 2 に、スキーマ検証によってスキーマの型注釈が各 XML 要素と属性に追加され、これらの型は DB2 の XML ストレージの中で持続します。例えば、ある XML スキーマが (ヒント 1 で示した) 部門表の従業員 ID を整数と定義しており、このスキーマに対して文書群を検証すると、DB2 は各文書の中の従業員 ID の型が xs:integer であることを記憶します。そうすると、ある従業員 ID に対して文字列比較を行おうとすると、クエリー実行時にタイプ・エラーで失敗します。

XML スキーマ検証は、XML 構文解析中のオプション動作です。さまざまなパフォーマンス研究によると、スキーマ検証が有効になっている場合には、一般的に XML 構文解析の CPU 負荷は大幅に高くなります。このオーバーヘッドは、XML 文書の構造やサイズによって、特に使用される XML Schema のサイズや複雑さによって、大幅に変化します。例えば、中程度に複雑なスキーマでスキーマ検証を行うと、CPU 使用率が 50% 高くなります。その XML 挿入が非常に I/O 動作が多いものでない限り、CPU 使用率の高さは挿入スループットの低さを意味します。

そのアプリケーションが、XML クエリーや XML スキーマへの準拠に関して、より厳密な型チェックを必要としているのかどうかを判断する必要があります。例えば、XML 文書を受信し、検証し、処理してからデータベースに保管するアプリケーション・サーバーを使っている場合には、おそらくそうした文書を DB2 で再度検証する必要はありません。アプリケーション・サーバーの段階で、そうした文書が有効であることは既にわかっているのです。あるいは、そのデータベースは、信頼できるアプリケーションから XML 文書を受け取るのかもしれません。場合によると、そのアプリケーションは皆さんが自分でコントロールしており、XML データが常に有効であることがわかっているのかもしれません。そうした場合には、より高い挿入パフォーマンスを得るために、スキーマ検証を避けるべきです。しかし、DB2 データベースが受け取る XML データのソースが信頼できず、DB2 のレベルでスキーマへの準拠を確認する必要がある場合には、そのために少し余分な CPU サイクルを費やす必要があります。

要約: 挿入パフォーマンスを高めるためには、本当に必要な場合を除き、DB2 でスキーマ検証を行うことは避けるべきです。

ヒント 7: XPath 式では、可能な限り完全指定されたパスを使う

次のような XML 列を持つ表を使って、

create table customer(info XML);

次のような構造の「customerinfo」文書を処理することを考えてみてください。

リスト 6. 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 も city を返します。最高のパフォーマンスを実現するには、* や // を使うよりも、完全指定したパスの方が適切です。パスを完全指定することによって、DB2 は対象の要素に直接アクセスでき、文書の中で無関係な部分をスキップできるためです。

言い換えると、求める要素が文書中のどこにあるかわかっている場合には、その情報を完全指定パスとして提供した方が有利です。/customerinfo/phone ではなく //phone と要求すると、文書中の任意の場所にある phone を要求することになります。そうすると DB2 は、その文書の「addr」サブツリーにまでナビゲートし、その文書の全レベルで phone 要素を探さなければなりません。これは回避すべきオーバーヘッドと言えます。

また、* と // は、望ましくない、あるいは予期しないクエリー結果を生ずる可能性があることにも注意してください。例えば下記のように、いくつかの「customerinfo」文書に「assistant」情報も含まれていたとしましょう。//phone というパスは、顧客の電話番号とアシスタンスの電話番号を、区別せずに返します。クエリー結果ではそんなことはわからず、誤ってアシスタントの電話番号を顧客の電話番号として処理してしまうかもしれません。

リスト 7. 文書中の別々のレベルにある 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」文書を顧客名で検索することが多いとしましょう。顧客名要素に索引を付けることによって、そうしたクエリーのパフォーマンスを大幅に改善することができます。次の例を見てください。

リスト 8. 顧客名での検索をサポートする索引
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 よりも大幅に大きくなる可能性があります。これは、//name という XML パターンによって、文書中のあらゆる場所にある name 要素が一致チェックされるためです。しかしアシスタントの名前で検索することがないのなら、アシスタンスに索引は必要ないのです。

読み取り操作に関しては、索引 custname1 の方が小さいため、おそらくパフォーマンスは custname2 よりも高くなります。挿入や更新、削除などの操作に関しては、索引 custname1 では顧客名のみに索引メンテナンスのオーバーヘッドが生じますが、索引 custname2 では顧客名とアシスタント名の両方に生じます。当然ながら、挿入、更新、削除に最高のパフォーマンスを求めるのであれば、余分なコストは無駄であり、アシスタントの名前による索引アクセスも必要ないはずです。

もう 1 つ、下記の「すべてを索引付けする」heavyIndex も考えてみてください。この索引の中には、すべてのテキスト・ノード、つまり XML 列の中にある、すべての XML 文書の中のすべてのリーフ要素の値が含まれています。こうした索引は、挿入や更新、削除などの操作を行う際のメンテナンスが非常に面倒であり、しかも大量のストレージ・スペースを必要とするため、通常は奨められません。唯一の例外は、書き込み動作が少なく、また詳細な索引を定義できず予測が難しいクエリー負荷を持つアプリケーションの場合です。

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

要約: XML 索引を定義する際には可能な限り厳密に行い、できれば * と // は避けるべきです。

ヒント 9: 文書をフィルターするための述部は、XMLQUERY の中ではなく XMLEXISTS の中に置く

次のような表とデータを考えてみてください。

create table customer(info XML);
表 2. 顧客表の 3 つのデータ行
<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;

しかしこのクエリーは、次のような理由から、求めるクエリーではありません。

  1. このクエリーは、表の中の行数と同じ行数を持つ、次の結果セットを返します。これは、この SQL 文には where 節がないため、他の行を削除できないためです。
    <name>Matt Foreman</name>

    3つのレコードが選択されました
  2. 表の中の、述部が一致しない各行に対して、空の XML シーケンスを含んだ行が返されます。これは、XMLQUERY 関数の XQuery 式は 1 度に 1 行 (文書) づつ適用されるだけであり、結果セットから行は削除せず、行の値を変更するだけだからです。この XQuery 式が生成する値は、述部が真ならば顧客の名前要素、そうでなければ空のシーケンスです。こうした空の行は、(SQL/XML 標準によれば) 意味としては正しく、クエリーがこのように書かれている場合には空の行を返す必要があります。
  3. このクエリーのパフォーマンスは、あまり高くないでしょう。第 1 に、このクエリーは行を削除することが許されていないため、/customerinfo/phone に索引があったとしても使うことができません。第 2 に、このクエリーは大量の空の行を返すため、必要以上に遅くなります。

パフォーマンスの問題を解決し、しかも求める出力を得るためには、XMLQUERY 関数を select 文節の中で使って顧客名のみを抽出するようにし、行を削除する検索条件は、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 つのレコードが選択されました

要約: XMLQUERY 関数の述部は XML の値の中でしか適用されないため、行は削除しません。文書や行をフィルタリングする述部は、XMLEXISTS 関数の中に入れるべきです。

ヒント 10: XMLEXISTS の中でブール述部となるのを避けるため、大括弧 [ ] を使う

よくある間違いとして、上記のクエリーを書く際に、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 つのレコードが選択されました

XMLEXISTS 述部の式は、XMLEXISTS が常に真として評価されるように書かれています。従って、どの行も削除されません。XMLEXISTS 述部が偽と評価されるのは、対象の行の中にある XQuery 式が空のシーケンスを返す場合のみです。しかし大括弧がないと、この XQuery 式はブール式となり、常にブール値を返し、空のシーケンスは返しません。XMLEXISTS が、本当に値の存在の有無をチェックすることに注意してください。もし値が存在すれば、たとえその値がブール値の「偽」であったとしても、真と評価してしまうのです。SQL/XML 標準では、この動作は正しいのです。ただし、そんなつもりで書く人はいないはずです。

この場合も、どの行も削除されないため phone の索引が使えず、必要のない大量の行を受け取る羽目になります。また、2つ以上の述部を使う際に、これと同じ間違いをしないように注意してください。下記のクエリーを見てください。

リスト 9. XMLEXISTS の中で 2 つの述部を使う場合の間違い
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 式は「exp1 and exp2」の形式なので、相変わらずブール式なのです。このクエリーを、行をフィルターでき、索引を使えるように適切に書くためには、次のようにします。

リスト 10. 行をフィルターし、索引が使える適切なクエリー
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 の中でブール述部を使ってはいけません。「and」や「or」を含めて、述部は大括弧の中に置く必要があります。

ヒント 11: RUNSTATS を使って XML データと索引の統計を集める

RUNSTATS ユーティリティーは、XML データや XML 索引の統計を収集できるように拡張されています。DB2 の、コストに基づくオプティマイザーは、こうした統計を使ってXQuery クエリーと SQL/XML クエリーのための効率的な実行計画を生成します。従って、リレーショナル・データに使う場合と同じように RUNSTATS を使うことができます。表の中にリレーショナル・データと XML データの両方が含まれており、リレーショナル統計のみをリフレッシュしたい場合には、新しい文節「EXCLUDING XML COLUMNS」を付けて RUNSTATS を実行します。この文節を付けないと、デフォルトで、そして望ましい動作として、常にリレーショナル・データと XML データの両方の統計が収集されます。

リレーショナル・データの場合も XML データの場合も、サンプリングを有効にすることで runstats の実行時間を削減することができます。大きなデータ・セットの場合、データの 10% (あるいはそれ以下) に対する統計でも、十分にデータ全体を表現することができます。どのようなサンプリング割合を選ぶにせよ、runstats では、行サンプリングも (Bernoulli サンプリング)、ページ・サンプリング (システム・サンプリング) も行うことができます。行レベルのサンプリングは、すべてのデータ・ページを読み取りますが、各ページの、ある割合の行のみ (つまり対応する XDA ページのサブセットのみ) しか考慮しません。ページ・レベルのサンプリングは、データ・ページの、ある割合しか読み取らないため、I/O を大幅に削減することができます。従って、表の中に XML データ以外に大量のリレーショナル・データも含まれている場合には、ページ・サンプリングによってパフォーマンスは大幅に向上します。しかし、リレーショナル・データの値が非常にクラスター化されている場合には、行レベルのサンプリングの方が正確な統計を生成するかもしれません。

以下にいくつかの例をあげます。最初の runstats コマンドはサンプリングせず、customer 表とその索引すべてに関して、最も包括的で詳細な統計を収集します。これを実行するための時間が許されるのであれば、この方法は理想的です。2 番目のコマンドは同じ統計を収集しますが、すべてのページのうちの 10% の統計しか収集しません。多くの場合、この方法でもオプティマイザーに与えられる統計の正確さは最初のコマンドと同程度ですが、結果はずっと早く返ってきます。3 番目のコマンドは、すべての行のうちの 15% をサンプリングし、分散統計は収集せず、また最初のコマンドと 2 番目のコマンドでは行わなかった、索引に対するサンプリングを行います。

リスト 11. 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);

要約: DB2 は、XML 統計が得られる場合には、より的確な実行計画を生成します。runstats は通常どおりに使こともでき、あるいは実行時間を削減するためにサンプリングを併用することもできます。

ヒント 12: SQL/XML 公開ビューを使ってリレーショナル・データを XML として公開する

SQL/XML 公開関数を使うと、リレーショナル・データを XML フォーマットに変換することができます。SQL/XML 公開関数をビュー定義の中に隠すと、アプリケーションや他のクエリーは作成された XML 文書を単純にビューから選択すればよく、自ら公開関数を処理する必要がなくなるため、有利になります。

リスト 12. ビューの中に隠された SQL/XML 公開関数
create table unit( unitID varchar(8), name varchar(20), manager varchar(20));

create view UnitView(unitID, name, unitdoc) as
   select unitID, name, 
       XMLDOCUMENT(
          XMLELEMENT(NAME "Unit",
          XMLELEMENT(NAME "ID", u.unitID),
          XMLELEMENT(NAME "UnitName", u.name),
          XMLELEMENT(NAME "Mgr", u.manager)
             )   )
   from unit u;

ビュー定義の中にリレーショナル列の一部を含めていることに注意してください。これは単なるビューであり、物理的にビューを作成したわけではないため、これによって物理的な冗長性が生ずることはありません。リレーショナル列を公開することによって、このビューを効率的にクエリーできるようになります。例えば、ある特定の組織単位 (unit) に対する XML 文書をフェッチする必要があるとしましょう。以下の 3 つのクエリーは、どれもそれを行いますが、3 番目のクエリーは他よりも優れたパフォーマンスを示す傾向があります。

最初の 2 つのクエリーでは、フィルタリング述部は作成された XML に対して表現されています。しかし、基礎となっているリレーショナル列あるいはその索引に XML 述部を適用することはできません。従って、この 2 つのクエリーは、すべての組織単位に対して XML を作成してから「WWPR」の XML を選択するようにようにビューに要求しているのです。これは最適な方法ではありません。

以下のパフォーマンスは最適ではありません。

リスト 13. 最適ではないパフォーマンスのクエリー
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;

3 番目のクエリーでは、リレーショナル述部を使うことで「WWPR」用の XML 文書のみが作られるようになっており、そのため (特に大きなデータ・セットでは) 実行時間が短くなります。このクエリーのパフォーマンスは優れています。

リスト 14. パフォーマンスの優れたクエリー
select unitdoc
from UnitView
where UnitID = "WWPR";

要約: リレーショナル列は、SQL/XML 公開ビューの中に含めるようにします。そしてビューをクエリーする際には、作成された XML に対して述部を表現するのではなく、こうしたリレーショナル列に対して述部を表現するようにします。

ヒント 13: XMLTABLE ビューを使ってリレーショナル・フォーマットで XML データを公開する

リレーショナル・データを XML フォーマットで公開するためにビューを作成すると便利なのと同じように、XML データをリレーショナル・フォーマットで公開するためにビューを使うこともできます。同じことはヒント 12 にも言えますが、順序は逆です。次の例を見てください。この例では、XML 文書から表の形式で値を返すために、SQL/XML 関数 XMLTABLE を使っています。

リスト 15. 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 列情報を含めていることに注意してください。例えば、ある ZIP コード (郵便番号) に対する顧客の ID と名前を、表形式のリストで取り出したいとします。下記の 2 つのクエリーはそれを行いますが、2 番目の方が最初のものよりもパフォーマンスが高いことが多いのです。最初のクエリーでは、フィルタリング述部が、XMLTABLE 関数が生成する CHAR 列「Zip」に対して表現されています。しかし、基礎となる XML 列やその索引にリレーショナル述部を適用することはできません。そのため、このクエリーはビューに対して、すべての顧客に対する行を生成してから zip コード「95141」に対応する顧客を選び出すように要求しています。これは最適ではありません。2 番目のクエリーは XML 述部を使うことによって、「95141」に対応する行のみが生成されるようにしており、その結果 (特に大きなデータ・セットでは) 1 番目のクエリーより実行時間が短くなります。

リスト 16. 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 と他の述部を適用し、最終的な結果セットを返します。

要約: リレーショナル形式で XML データを公開する XMLTABLE ビューに注意すること。できればビュー定義の中に追加の列を含めるようにし、フィルタリング述部を、XMLTABLE 列に対してではなく、追加した列に対して表現できるようにします。

ヒント 14: 短いクエリーや OLTP アプリケーションでは、パラメーター・マーカーを持つ SQL/XML 文を使う

非常に短いデータベース・クエリーは非常に高速に実行されるため、そうしたクエリー全体としての応答時間の大きな部分を、コンパイルと最適化のための時間が占めることになります。従って、そうしたクエリーを 1 度だけコンパイル (「準備」) し、述部のリテラル値のみを実行ごとに渡すと有効です。DB2 9 の XQuery は外部パラメーターをサポートしていませんが、SQL/XML 関数の XMLQUERY と XMLTABLE、そして XMLEXISTS はサポートしています。これらの関数では、SQL パラメーター・マーカーを、組み込まれた XQuery 式の中に変数として渡すことができます。短いクエリーを繰り返すアプリケーションでは、この方法が推奨されます。

リスト 17. ハードコーディングされた述部のリテラル値
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")
リスト 18. パラメーター・マーカーを使う
select info 
from customer
where xmlexists('$i/customerinfo[phone = $p]' 
                passing info as "i", cast(? as varchar(12)) as "p")

要約: 短いクエリーや OLTP トランザクションは、パラメーター・マーカーを付けた文を用意することで、より高速になります。これは XML の場合、SQL スタイルのパラメーターを SQL/XML で XQuery 式に渡す必要があるということです。

ヒント 15: XML の挿入中と取得中にはコードページ変換を避ける

XML は、内部エンコードも外部エンコードも可能なことから、DB2 の他のデータ型とは異なります。内部エンコードというのは、XML データのエンコードを XML データそのもので行うという意味です。一方、外部エンコードというのは、外部情報によってエンコードを行うという意味です。どのようなエンコーディングが行われるかは、DB2 との XML データ交換に使われるアプリケーション変数のデータ型によります。アプリケーションが XML に対して文字型変数を使う場合には、外部で、つまりアプリケーションのコード・ページの中でエンコードされます。もしバイナリーのアプリケーション・データ型を使う場合には、その XML データは内部でエンコードされると考えられます。内部エンコードということは、Unicode の BOM (Byte-Order mark)、または XML 文書自体のエンコーディング宣言によってエンコーディングが決まることを意味しています。下記はエンコーディング宣言の例です。

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

パフォーマンスの観点から言えば、コード・ページ変換は余分な CPU サイクルを消費するので、できるだけ避けるべきです。内部エンコードされた XML データは不必要なコード・ページ変換を防止できるため、外部エンコードされたデータよりも好ましいです。つまりアプリケーションの中では、バイナリーのデータ型の方が文字型よりも好ましいということです。例えば CLI で、SQLBindParameter() を使って、パラメーター・マーカーを入力データ・バッファーにバインドする際には、SQL_C_CHAR や SQL_C_DBCHAR、SQL_C_WCHAR などよりも、SQL_C_BINARY データ・バッファーを使うべきです。また Java アプリケーションから XML データを挿入する場合には、XML データをバイナリー・ストリーム (setBinaryStream) として読んだ方が、ストリング (setString) として読むよりも適切です。同様に、Java アプリケーションが DB2 から XML データを受け取ってファイルに書き込む場合には、もしその XML が非バイナリー・データとして書かれていると、コード・ページ変換が起こる可能性があります。

DB2 から取り出した XML データをアプリケーションで使う場合、その XML データはシリアライズされます。シリアライズは XML 構文解析と逆の操作です。シリアライズは、DB2 の内部 XML フォーマット (構文解析され、ツリー状に表現されています) を、アプリケーションが理解できるテキスト形式の XML フォーマットに変換するプロセスです。ほとんどの場合、DB2 に暗黙的なシリアライズを行わせるのが最善です。これはつまり、次の例のように SQL/XML 文は単純に XML 型の値を選択するだけであり、アプリケーション変数へのシリアライズは、DB2 が可能な限り効率的に行うということです。

リスト 19. 暗黙的なシリアライズを行うクエリー
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 のヒントのすべてを適用する必要はありません。1 つか 2 つを適用するだけでも、状況を大きく改善することができます。例えば、不必要なコード・ページ変換を減らしても、表スペース構成上の問題から非常に I/O 負荷が高いシステムでは、あまり効果はありません。同様に、SQL/XML パラメーター・マーカーを使っても、より良いクエリー実行計画を実現するために runstats を実行する必要がある場合には、クエリーのパフォーマンスは改善されません。つまり、この記事で取りあげたのは、パフォーマンスの問題を避けるためのヒントなのです。既に起きているパフォーマンスの問題を解決するためには、まず根本原因とボトルネックを特定する必要があります。DB2 の visual explain や db2exfmt、スナップショット・モニターなどの標準診断ツールは、リレーショナル・データの場合とまったく同様に、XML パフォーマンスの問題を調べるためにも有効です。


謝辞

この記事の作成にあたって協力くださった、Cindy Saracco 氏と Irina Kogan 氏、Henrik Loeser 氏、Nikolaj Richers 氏、そして Marcus Roy 氏に感謝いたします。

参考文献

学ぶために

  • XML 関連の記事
    • Cynthia M. Saracco 著による「What's new in DB2 Viper - XML to the core」は、Viper の新しい XML 技術と、なぜ IBM が DB2 を「混成」、つまり複数の構造を持った DBMS (database management system) と考えるようになったのかを解説しています。
    • Cynthia M. Saracco 著による「SQL を使った DB2 XML データの照会」では、XML 列に保管されたデータを SQL と SQL/XML を使ってクエリーする方法を学ぶことができます。
    • Don Chamberlin と Cynthia M. Saracco の共著による「Query DB2 XML data with XQuery」では、XML 列に保管されたデータを XQuery を使ってクエリーする方法を学ぶことができます。
    • Matthias Nicola と Bert van der Linden の共著による「XML Support in DB2 Universal Database」を読んでください。
    • Matthias Nicola と Fatma Ozcan の共著による「DB2 9 での pureXML: XML データのクエリーに使う手段を使い分ける」を読み、XML データのクエリー方法に関する情報を得てください。
    • Irina Kogan と Matthias Nicola、Berni Schiefer の共著による「「DB2 9 の XML パフォーマンス特性」を読んでください。セキュリティー・ブローカーのトランザクション処理環境を DB2® 9 XML や IBM POWER5+、AIX 5.3、TotalStorage DS8100 などを使ってシミュレーションした場合のパフォーマンスとスケーラビリティーの特性を学ぶことができます。
    • Matthias Nicola と Uttam Jain の共著による「DB2 9.5 で XML を更新する」を読んで、DB2 上で XML データを効率的に変更する方法を学んでください。
    • Matthias Nicola と Vitor Rodrigues の共著による「XMLTABLE の例証」で、SQL/XML を使って XML データを操作する強力な手法を調べてください。
    • Matthias Nicola と Jasmi John の共著による「XML Parsing: A Threat to Database Performance」を読んでください。データベース・システムでの XML パーサーのパフォーマンスを考える上で、最も有望な研究対象について学ぶことができます。
    • DB2 (pureXML) Wiki を訪れ、XML に関する DB2 の最新技術を知ってください。
  • Fraser McArthur 著による、「Best practices for tuning DB2 UDB v8.1 and its databases」を読んでください。DB2® UDB データベースとそのアプリケーションから最適なパフォーマンスを得るためのヒントを得ることができます。
  • Scott Hayes 著による「Top 10 performance tips」を読んでください。Unix や Windows、OS/2 での、DB2 UDB による e ビジネス OLTP アプリケーションのパフォーマンスのヒントを学ぶことができます。
  • Yongli An と Peter Shum の共著による「DB2 Tuning Tips for OLTP Applications」は、OLTP (Online Transaction Processing) 型のパフォーマンス・ベンチマーク (TPC-C や TPC-W、Trade2 など) の実行から学んだ教訓に基づいて、DB2 のチューニングに関するいくつかのヒントに焦点を当てています。データベース・アプリケーションのパフォーマンスは、多くの要因に影響を受けます。この論文は DB2 の構成面を中心に議論を進めています。
  • 「Tips for improving INSERT performance in DB2 Universal Database」の中で、著者の Bill Wilkins は、挿入が行われる際に具体的に何が起きるのか、その代替手段、そして挿入のパフォーマンスに影響する問題 (ロックや索引の保守、制約管理など) などについて解説しています。
  • Allen Lee 著による「Improve database performance on file system containers in IBM DB2 UDB V8.2 using Concurrent I/O on AIX」を読んでください。AIX で利用できるさまざまなアプリケーション I/O モデルについて、また DB2 が CIO 機能を活用していることを理解できます。
  • Larry Pay 著による「DB2 UDBバージョン8.2のRUNSTATS」を読んでください。最適なデータベース・パフォーマンスを得るための RUNSTATS の使い方を、従来からのオプションと新しいオプションと合わせて学ぶことができます。
  • Xiaomei Wang と Wini Mark、Ken Lau、Raul F. Chong の共著による「DB2 UDB OLTP tuning illustrated with a Java program」は、IBM® DB2® の UDB (Universal Database™) データベース・サーバーをモニターし、チューニングするために守るべきテクニックを、順を追って説明しています。
  • developerWorks の developerWorks resource page for DB2 for Linux, UNIX, and Windows を訪れて記事やチュートリアルを読み、また他のリソースと関連付けて、皆さんの DB2 スキルを向上させてください。
  • DB2 コミュニティーのための DB2 Express Edition の無料版、DB2 Express-C について学んでください。

製品や技術を入手するために

  • The DB2 Snapshot Monitor を訪れ、このツールの最新情報を知り、またこの製品を入手して下さい。
  • DB2 Enterprise 9 の無料の試用版をダウンロードしてください。
  • DB2 が無料で使えるようになりました。DB2 コミュニティーのための DB2 Express Edition の無料版、DB2 Express-C をダウンロードしてください。これは DB2 Express Edition と同じコア機能を提供しており、アプリケーションを構築し、デプロイするための堅固な基礎とすることができます。
  • DB2 のための統合開発環境として、DB2 Developer Workbench をダウンロードしてください。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Information Management, XML
ArticleID=271566
ArticleTitle=DB2 での pureXML パフォーマンスのための 15 のベスト・プラクティス
publish-date=05262009