mb_language("uni"); mb_internal_encoding("utf-8"); mb_http_input("auto"); mb_http_output("utf-8"); ?>
2005年3月22日 更新
RDBと同じく、XML DBもデータ構造がパフォーマンスに影響を及ぼす。XMLの構造を見直すことで、クエリを大幅に簡略化することも可能なのだ。今回は、効率的なXML構造の書き方について、XprioriおよびNeoCore(以下便宜上Xprioriと総称)を使って説明する。
クエリの対象となるXML文書のサイズは、当然のことながらクエリの実行速度に影響を及ぼす。だが、クエリの書き方によって、サイズによる影響を受ける度合いが大きく異なるのも事実である。単純に特定の要素名を検索するだけなら、XML文書のサイズによる性能低下はあまり生じない。しかし、式の評価を行うと、大きな性能低下が発生する。
この現象を実際に確かめるため、以下のテストデータを用意した。list要素の子要素として、10万個の要素が含まれている。
<?xml version="1.0" encoding="utf-8"?> <list> <e0>0</e0> <e1>1</e1> ......(中略)...... <e99998>99998</e99998> <e99999>99999</e99999> </list>
一方、小サイズのXML文書として、上記XML文書の中央部分の100要素だけを残して、ほかのlist要素の子要素を削除した文書を用意した。つまり、こちらのXML文書にはlist要素の子要素として、100個の要素が含まれている。
この2つのXML文書に、以下の2種類のクエリを適用してみる。
/ND/list/e50000
/ND/list/*[.='50000']
このクエリを10~10000回程度繰り返し呼び出し、所要時間を計測するプログラムをVisual Basic .NETで作成してみた。
Module Module1 Sub Main() Const loopCount1 As Integer = 10000 '処理時間に応じて調整する Const loopCount2 As Integer = 10 '処理時間に応じて調整する Dim api As New NEOCOREXMSLib.NeoCoreAPIClass api.connectToNeoCoreXMS("localhost", 7701) api.login("Administrator", "SamplePass") Dim start1 As DateTime = DateTime.Now Dim result1 As String For i As Integer = 1 To loopCount1 result1 = api.queryXML("/ND/list/e50000") Next Dim end1 As DateTime = DateTime.Now Console.WriteLine(result1) Dim start2 As DateTime = DateTime.Now Dim result2 As String For i As Integer = 1 To loopCount2 result2 = api.queryXML("/ND/list/*[.='50000']") Next Dim end2 As DateTime = DateTime.Now Console.WriteLine(result2) Console.WriteLine(end1.Subtract(start1).TotalMilliseconds() / loopCount1) Console.WriteLine(end2.Subtract(start2).TotalMilliseconds() / loopCount2) End Sub End Module
ぞれぞれの処理時間(単位ミリ秒)は、以下のとおり(使用環境によって、時間は変化する可能性がある)。
● /ND/list/e50000
小サイズXML文書:1.11
大サイズXML文書:1.14
● /ND/list/*[.='50000']
小サイズXML文書:2.27
大サイズXML文書:1075
要素名を問い合わせるクエリ(/ND/list/e50000)は、要素数が100個でも10万個でも、実行時間に大差はない。しかし、イコール演算子によって値の判定処理を行っているクエリ(/ND/list/*[.='50000'])は、実行時間が3桁も異なるという結果が出た。
このことから、多数のデータを扱うデータベースを設計する際は、値を判定して区別するクエリを少なくすると、高速化できる可能性があることが分かる。また、値を判定するクエリを多用するデータベースは、小さなテストデータで上手く動作しても、巨大な実運用データを与えると速度が大幅に低下する可能性がある。事前に十分なサイズのデータを与えての検証が必要ともいえる。
XML文書の構造によって、性能面で大きな相違が生じる場合がある。例えば、日付の年月を記述する2種類のXML文書があるとする。性能面で有利なのはどちらだろうか。
<?xml version="1.0" encoding="utf-8"?> <list1> <date>2001/01</date> <date>2001/02</date> ......(中略)...... <date>2084/04</date> </list1>
▲XML文書A
<?xml version="1.0" encoding="utf-8"?> <list2> <date><year>2001</year><month>01</month></date> <date><year>2001</year><month>02</month></date> ......(中略)...... <date><year>2084</year><month>04</month></date> </list2>
▲XML文書B
一見して、よりコンパクトに記述されているXML文書Aの方が、より高速に処理できそうな印象を受ける。年と月を1つの文字列としてしか扱わないのであれば、必ずしも間違いではない。しかし、年と月を分けて処理しようとすると、大きな性能差が生じる。
例えば、2005年の1~3月の要素だけを取り出すクエリを記述してみる。XML文書Aに対しては、以下のクエリで実現できる。
/ND/list1/date[substring(.,1,4)='2005' and substring(.,6,2)>='01' and substring(.,6,2)<='03']
一方、XML文書Bについては以下のクエリになる。
/ND/list2/date[year='2005' and month>='01' and month<='03']
「XML文書のサイズが及ぼす影響」で使用したのと同様のプログラムで、この2つのクエリの処理時間を計測してみると、前者は74.6ミリ秒、後者は6.05ミリ秒という結果を得た(使用環境によって、時間は変化する可能性がある)。つまり、1けたの性能差が生じたことになる。
一般的に、クエリで独立した処理を行う単位で要素や属性に分けると効率が上がるといえる。クエリの中でデータを分解したり組み立てるような処理を記述するのは、できるだけ避けた方がよい。
XMLにはデータを記述する手段として、「要素」と「属性」という書式が存在する。単純なデータであれば、どちらを使用しても問題なく記述できる。また、Xprioriのクエリは、どちらでも処理性能に大きな差は出ない。しかしだからといって、どちらを使っても同じというわけではない。要素と属性は、それぞれに異なる特性がある。それらの特性を生かすことを意識することで、適材適所の使い分けができる。
まず、要素は子要素を持つことができるが、属性は子要素を持つことができない。子要素を持たせる可能性がある情報は、要素を使って記述しなければならない。例えば、名簿に名前を記述する際に、要素として記述した場合と属性として記述した場合の例を以下に示す。
<名簿><名前>山田太郎</名前></名簿>
名前を要素として記述した場合
<名簿 名前="山田太郎"></名簿>
名前を属性として記述した場合
これらのXML文書で、名前を「姓」と「名」に分ける変更が必要になったらどうなるだろうか。要素を使って記述していれば、容易に変更できる。
<名簿><名前><姓>山田</姓><名>太郎</名></名前></名簿>
しかし、XMLの構文上の制約から、属性を用いて以下のように記述することはできない。
<名簿 名前="<姓>山田</姓><名>太郎</名>"></名簿>
また、要素には順番があるが、属性には順番がない。例えば、以下の2つのXML文書は、子要素の並び順が異なるため、異なる内容のXML文書と見なされる。
<要素><子要素1/><子要素2/></要素>
<要素><子要素2/><子要素1/></要素>
この場合、子要素の順番を判定するクエリも記述できる。例えば、最初の子要素を得る
/ND/要素/*[1]
というクエリは、最初の例では子要素1を、2番目の例では子要素2を得ることができる。しかし、属性には順番が存在しない。つまり、以下の2つのXML文書の違いを検出するクエリを記述することはできない。
<要素 属性1="......" 属性2="......" />
<要素 属性2="......" 属性1="......" />
このように、順番を明示的に指定できるか否かで、要素と属性は大きく異なっている。順番に重要な意味があるデータを記述する場合は、属性ではなく要素を使うべきだろう。
Webブラウザからコンソールにアクセスしてクエリを実行した場合、なかなか結果が表示されないことがある。もちろん、大量のデータに対して複雑なクエリを実行すればクエリの処理に時間が掛かるのはいうまでもない。しかし、Webブラウザ側の処理の重さが問題になる場合がある。
Internet Explorerは検索結果のXML文書をDOMに読み込んでから表示するため、クライアント側のリソースを大量に消費し、表示に時間がかかる場合がある。これはInternet Explorerの仕様であるため、残念ながら回避する手段がない。大量の結果が返されるようなクエリはできるだけ行わない方がよい。
サーバのメモリに展開されたクエリ結果のサイズが上限を超えると、「Result Set Too Large」というエラーが発生する。
これを回避するには、Xprioriをインストールしたディレクトリ下の、neoxml\config\NeoServer.xmlファイルを変更する。具体的には、XmlBufferSize要素の値やBufferPool要素のSize子要素の値を拡大するのである。
▲このページのTOPへ