2007/10/13
DB2 9 を利用したメール・アーカイブ・システムの開発 (3)
データベースの設計
前回は、メールのデータ化=XMLスキーマの構造定義について説明してきました。今回は、XMLデータベースでどのように設計が変わるのか?という話に触れてみたいと思います。
私も長く業務システムの開発をしておりますので、過去いろいろなシステムの設計を行ってきました。その自分がもし従来のリレーショナルデータベース(RDBMS)でメール・アーカイブ・システムを開発したらどうなるか?という問題を考えてみたのです。
とは言うものの、メールアーカイブを開発するなら最初からDB2(ハイブリッドDB)を使ってみようと思っていたので、この数ヶ月はそういうことを考えることすらありませんでした。
ここで、少しER図が出てくるんですが、これは私がこの7~8年前から付き合っている「T字形ER」というデータベース設計手法に基づいているものです。(なじみの無い方はごめんなさい。今時はやはりUMLなんでしょうかね?)
ちょっと前後するんですが、テーブルの設計を行う前にクエリーの指示画面のイメージを示しておきたいと思います。下図は実際のアプリケーションの画面イメージです。
さしあたっては、どんな項目を検索項目にしているかを見て頂ければと思います。

リレーショナルDBで行った場合の表設計
メールアーカイブは、その主たる目的がメールデータをデータベースに格納することなので、関連するテーブルがいくつも出てくるわけではありません。ちょっと物足りないかもしれませんが、単純なパターンとして考えてみてください。
まず、ひとつのテーブルを作ってそのカラム(属性)を定義してみたとします。
主キーたるIDがあり、その属性としては、タイトル、送信者、宛先、日付、本文等の項目でしょうか。
下図は、「メール」エンティティを定義したもので、更にCC属性や、メッセージID、IN_REPRY_TO属性、ファイル名、メールファイルそのもの(BLOB)、ファイルのタイムスタンプ等を定義しています。

メッセージID(Message-Id)とIN_REPRY_TO属性を入れたのは、メールの関連表示をさせる際に必要となる属性値なので、ヘッダ項目の中でもより重要視したためです。
上の例では、他のヘッダ項目(Received,Reply-To,X-Mailer,...)やエンベロープの情報はカットしてしまっています。これは重要性が無いと判断したからです。もしそれらもカラムに追加するとしたらどうなるでしょうか?前回のスキーマ定義で紹介したようにサポートしているヘッダ属性だけでも約40種ほどあるのですが、それらを全てカラムとして定義すべきでしょうか?「Received」属性などのように複数件の繰返すものもあります。
これらの解決案としては、仮想エンティティとして別テーブル(MAIL_HEADER)を定義することなどが考えられます。
上の例では、添付ファイルが考慮されていません。また、本文はテキストとHTMLの両者を持つ場合もあります。これらを解決するためには、やはり別テーブル(ATTACH_FILES)を定義し、テキスト/HTML/添付ファイルの区分項目、Content-Type、CharSet、Content-Transfer-Encodingなどの属性、そして本文などのテキスト情報を定義すると良いかも知れません。
これらの解決案が次のER図です。

そうですね。これで、まぁまぁいけるんじゃないでしょうか?
もし実際にRDBMSで設計しろと言われたら、私はこうすると思います...。
DB2 9で行った場合の表設計
DB2 9を紹介する際に出てくる、いわゆる「おきまり」のパターンですね。
図にすると次のようになります。

つまり、主キーのIDとXMLタイプのカラムだけの表です。
いやぁ、ほんとシンプル!
これでもなんとかなることはなるんですが、実際には幾つかのカラムが追加定義されています。その代表的なものを列挙してみます。(タイトルは付けたカラム名)
- MAIL_DES
メールデータの識別子(Designator)で、メールレシーバが付けたユニークな文字列を格納します。
主キーのMAIL_IDはいわゆる代理キー(サロゲート・キー)で数値の連番を付番します。このカラムは同じメールメッセージファイルかどうかを特定するために必要となります。
ユニークキーとして定義しています。 - MESSAGE_ID
メールヘッダ属性の「Message-Id」を格納します。
本来メールを特定するIDですから、全世界のメールサーバは1通毎にユニークな文字列を付加しなければならないはずなのですが、SPAMメールなどは毎日同じIDで送ってきたりします。
当システムでは敢えてこの項目にユニークなインデックスを付加しています。このことにより、同じ「Message-Id」はDBMSの制約によりはじかれることになります。例えば、社内の複数の人に同報でメールが届くと、メールファイルは複数件作成されますが、アーカイブのレコードは1件だけ登録されて残りは制約によりはじかれることになります。でもヘッダの「To」、「Cc」やエンベロープの「RCPT_TO」でBCCも含めて宛先は分かりますから、問題無いと判断しています。 - IN_REPLY_TO
メールヘッダ属性の「In-Reply-To」を格納します。
返信メールを書くと、元のメールの「Message-Id」がこの属性に記述されます。(「In-Reply-To」ではなく、属性「References」に記載される場合もあるので、実際はちょっと単純ではないですが・・・。)
やはりこのカラムについても、インデックスを付加しています。
このカラムから該当する「Message-Id」を辿っていくと、スレッドの最初(ルート)のメールに行き着きます。その後、順に「Message-Id」を参照しているメールを探すと下図のような関連メールのツリー(スレッド)表示が可能となります。
- TIMESTAMP
この項目はXMLに変換されたファイルのタイムスタンプを格納しています。 - MAIL_DATE
メールの日付("Sat, 13 Oct 2007 22:22:22 +0900"等)をGSTに直し、「YYYYMMDDHHMMSS」のような形式に直したものを格納しています。それによって、日付範囲の検索が高速に行うことが可能になります。
実際の検索指示画面では、西暦の日付を指定します。(タイトル:「送信日付の範囲」) - DATE_ATTR
時々、未来の日付を付けて送ってきたりするメールがあると思うんですが、それをまじめに日付に直してしまうと嫌じゃないですか・・・。そんなとき、「Received」あたりから正しい日付時刻を推定するんですが、そういう処理をしたかどうかの属性を格納しています。 - MAIL_SIZE
メールのサイズです。これはXML内の要素には持っていない属性なので、追加しています。
実際の検索指示画面では、数値でサイズを指定します。(タイトル:「メールサイズ範囲」) - CNT_FILES
添付ファイルの数です。メールを解析し、レコードをセットする際に添付ファイルの数をセットしています。
実際の検索指示画面では、数値でファイル数を指定します。(タイトル:「添付ファイル数の範囲」) - BODY_TYPE
本文がテキストなのか、HTMLなのか、はたまたその両者なのか、そういう属性値をセットしています。
時々本分の無いメールも来たりすることないですか?
ということで、実際はもう少し複雑な形になるわけです。

設計及び試作段階では、カラムを追加してみたり削除したりすることは結構あると思います。
当然ながらXMLのスキーマを変更して要素などを追加することも可能ですから、どちらが良いかは検討する必要があると思います。
ところで、クエリーを行うに当って、問題になるのはパフォーマンスですが、そのために索引(インデックス)の定義は重要です。XMLカラムの特定パスに対しても索引は作成できるのですが、テーブルの索引のパフォーマンス(ストレスは全く無し)には敵わないと思います。
上の例では、関連メールを検索するための索引を2つ程作成しているのですが、当初はXML索引でやっていました。しかし、どうしても思うようなパフォーマンスが出なかったのですが、現在のような構造にすることで解決しています。
逆に、表題、差出人、宛先などの情報は、クエリー結果を表示させる上で良く使うものですが、それらはカラムに展開しておく必要は無いと思います。
RDBMSでは、項目長とか、データによってヌルになってしまう項目も結構気になるんですけど、XMLの場合はその煩わしさはあまり無いですよね。
まとめ
今回は、テーブルの設計を行う上で、RDBMSの場合とハイブリッドDBを使う場合を比較してみましたが、如何だったでしょうか?
多少の雰囲気の違いを知っていただければ幸いです。
ハイブリッドDBとしてのDB2 9の使い方は、いろいろなパターンがあるのではないかと思います。
今回の例は、ドキュメントとしてのメールデータを扱うというものでした。もっと定型的な業務システムの中で使う場合を考えたら、違った設計になってくることも十分に考えられます。
最後に、(全てケースを想定したものではないことをお断りして)考え方などについて、まとめておきたいと思います。
- ドキュメントを識別するためのプライマリーキーとXMLタイプのカラムで構成されたシンプルな表を基本として考える
- データを特定するような属性や他のテーブルと連関する属性などは、インデックスを作成するなど、テーブルやテーブル間の制約を持たせるカラムとすることを検討する
- サイズやタイムスタンプなど、ドキュメントそのものの属性を格納するカラムの必要性を検討する
- どのようなクエリーをさせたいのかを検討し、必要な検索項目が過不足なくあるかを確かめた上で、検索項目がXMLで格納される形式よりも、並べ替えや処理しやすい形式にすべきものは無いかなどを検討する
