はじめに
パフォーマンス要件はシステムを構築する際に、考慮すべき最も重要なポイントの1つです。リレーショナルデータベース(以降、RDB)に関しては、そのチューニングのポイントについては、書籍、Webなどで数多くの情報が存在しますが、XMLDBに関してはまだまだこれからといったところです。そこで本稿では、Cyber Luxeonに限らず、各XMLDB製品に共通するであろうパフォーマンスのポイントについて、実際に実行・測定し検証を試みました。
なお、XMLの更新機能に関してはXQuery Update Facilityの仕様策定が進められていますが、執筆段階では草案段階であり、XMLDBごとにXML更新用の機能が提供されているのが現状です。そこで本稿ではXMLDBの検索機能に対象を絞って説明を進めます。
対象読者
XMLに触れたことがある方、RDBなどデータベースを操作したことがある方を対象とします。
検証環境
まずは、本稿の検証に使用したハードウェアスペック、ソフトウェアを以下の表に示します。
CPU | Intel Core2 Duo E6300 1.8GHz |
メモリ | 2G |
OS | Windows XP |
Java | 1.6.0 |
DB | Cyber Luxeon ver2.0 Developer Edition |
XPath、XQueryの記法によるパフォーマンスの違い
テストデータ
XPath、XQueryの計測では、以下に示したXMLを共通で使用しました。1つのXMLファイルのように書かれていますが、実際は、item
要素をルート要素とする10000個のXMLと、1つのバインダドキュメント(複数のXMLにショートカットを設定することによって、あたかも1つのXMLファイルかのように扱えるCyber Luxeonの機能)を使用しています。バインダドキュメントはMultiDoc_Container
要素をルート要素とします。item
要素は1万件作成されており、item
要素内のprice要素にはランダムに整数値が格納されています。
<MultiDoc_Container> <item> <item_name>item1</item_name> <price>1100</price> </item> <item> <item_name>item2</item_name> <price>3000</price> </item> <item> <item_name>item3</item_name> <price>3000</price> </item> <!-- 以降10000件まで格納 --> </MultiDoc_Container>
測定方法
以下のJavaプログラムを使用して、実行時間を測定しました。検証にあたってはソースコード内のXQueryの文字列の箇所を検証ケースごとに、書き換えて使用しました。
public class ExecXQuery { public static void main(String[] args) { // XMLストアへの接続 clientSession = XlnClientSessionFactory.getSession(); XMLStore xmlStore = clientSession.getXMLStore( "test_store" ); // XQuery文字列(この部分をテストケースによって変更する) String xqueryStr = " for $item in /MultiDoc_Container/item\n" + " where $item/price > 1000\n" + " return $item"; XMLDocument xmlDoc = (XMLDocument)xmlStore.getFile( "/test_dir/dc.bnd" ); XQuery xQuery = clientSession.createXQuery( xqueryStr ); // 測定開始 long start = System.currentTimeMillis(); // XQuery実行 xQuery.execute( xmlDoc ); long end = System.currentTimeMillis(); // XQuery結果の取得 XQuerySequence results = xQuery.getResults(); XQuerySequenceIterator iter = results.getIterator(); int i = 0; while( iter.hasNext() ){ i++; XQueryItem xQueryItem = iter.nextItem(); Node node = (Node)xQueryItem.getValue(); } long allEnd = System.currentTimeMillis(); clientSession.release(); // 測定結果 clientSession.release(); System.out.println( "Query time:" + (allEnd -start) ); System.out.println( "All time:" + (allEnd -start) + "/検索ノード数:" + i ); } }
検証ケース1:XPathで「//ノード名」を使うと遅い
仮説
XPathの検索では、ノードへのパスをすべて記述しなくても、//ノード名というかたちで検索対象ノードを指定することにより、コンテキストノード配下の該当するノードすべてを指し示すことができます。しかし、これを使用するとノードの連なりを順にたどるという処理(ノードウォーキング)が実行されてしまい、パフォーマンスが悪化することが予想されます。
抽出対象データ
item
要素のprice
要素が「1000」である商品名要素を抽出します。
検証用XQuery
以下に測定で使用したXQuery式を示します。2つのXQuery式は同じ結果を返します。
for $item in /MultiDoc_Container/item where $item/price = 1000 return $item/name
for $item in //item where $item/price = 1000 return $item/name
測定結果
各XQueryで1万要素を検索した結果を以下の表に示します。
テストケース | XQuery実行時間 | 全実行時間 | 該当ノード数 |
パス直接指定 | 234ミリ秒 | 234ミリ秒 | 291 |
「//ノード名」指定 | 313ミリ秒 | 313ミリ秒 | 291 |
予想通り、パスを直接指定した方が、実行時間が少なく済んでいます。
考察・結論
XPath式で「//ノード名」は、パフォーマンスが劣化するので、必要性がない限り、なるべく使用しない方が良いでしょう。今回のケースでは「//ノード名」に該当するノードは、コンテキストノード配下では1つでしたが、コンテキストノード配下に同一名のノードが複数ある場合にも、パスを直接指定してOr条件(||)で連結するなどした方が、パフォーマンスは向上するでしょう。検索対象のXMLのノードが多くなるほど、たどるノードも多くなるので、パフォーマンスの差も顕著にあらわれます。
検証ケース2:XPATHで1度で絞り込んだ方が、WHERE文で絞り込むよりも速い
仮説
ノードを条件で絞り込んで検索する場合に、XPathに検索条件を指定して1度で対象ノードを絞り込むパターンと、上位ノードをXPathで抽出してから、WHERE句を使用して、ノードを絞り込む場合では、1度で絞り込んだほうが、無駄なノードを変数に格納しない分性能が良いことが予想されます。
抽出対象データ
item
要素のprice
要素が「1000」である商品名要素を抽出します。
検証用XQuery
以下に測定で使用したXQuery式を示します。2つのXQuery式は同じ結果を返します。
for $item in /MultiDoc_Container/item[price=1000] return $item/name
for $item in /MultiDoc_Container/item where $item/price = 1000 return $item/name
測定結果
各XQueryで1万要素を検索した結果を以下の表に示します。
テストケース | XQuery実行時間 | 全実行時間 | 該当ノード数 |
XPathで絞り込む | 219ミリ秒 | 219ミリ秒 | 291 |
Where句で絞り込む | 234ミリ秒 | 234ミリ秒 | 291 |
予想通りXPathで1度で絞り込んだ方が、実行時間は少なくすんでいます。
考察・結論
XPathで抽出する時点で絞り込んだ方が性能が良いという結果になりました。それは、XPathで絞り込まないパターンの場合、一旦ノードを変数に格納し、その上で絞り込むので、ノードがメモリに展開される分、オーバーヘッドがあるためと考えられます。余計なオーバーヘッドを生まないためにも、XPath上で絞り込める場合は、そのようにした方が良いでしょう。