このブログの更新は Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama

メールでのご連絡は hiyama{at}chimaira{dot}org まで。

はじめてのメールはスパムと判定されることがあります。最初は、信頼されているドメインから差し障りのない文面を送っていただけると、スパムと判定されにくいと思います。

参照用 記事

唐突に MongoDB の話

「CatyのJSONストレージとクエリ言語」で触れたように、MongoDBを触ったり調べたりしています。Kuwataさんも何やら書いてますね

MongoDBの入門的な解説もいずれ書いてみたい気もしますが、今日はそういうのはすっ飛ばした話をします。タイトルの「唐突に」とは、MongoDBの紹介も説明もないよ、ってことです。あしからず。

データベースと名前空間

MongoDBは、パフォーマンスにこだわっていて、機能性、使い勝手、整合性などを犠牲にしています。ここまで犠牲を払って、もし遅かったらゴミですね。もちろん、ゴミじゃないだろうと僕は期待してます。

データベースの実体は、mydb.0, mydb.ns のような2つのファイルです。名前からして、mydb.0にコンテンツ、mydb.nsに名前空間(NSと略記)の情報でしょう。NSってのがまた珍妙な概念ですが、概念的にはIDの一意性のスコープみたいなものです。コレクションはNSですが、インデックスもNSです。1データベースあたり、デフォルトで2万4000NS。コレクションへのアクセスを高速化したいなら1つのコレクションに数個のインデックスが付くでしょうから、1データベース当たり5000コレクションくらいかもしれません。

1コレクションあたりのドキュメントの最大個数はよくわからん(調べがついてない)ですが、1ドキュメントの最大サイズは4メガ。これ以上のでかいデータはチャンキングします。GridFSってのがチャンキングの実例。

データベースのディレクトリ/ファイルはなるべく速いディスクに置いて、ファイルがディスクの連続領域に配置されるようにしておきましょう。デフォルトで、*.0が64メガ、*.nsが16メガの領域を確保するので、最初から必要そうなデーターベース/サイズのディスク領域を割り付けておくとよさそうです。

BSON

BSONは名前も構造もJSONと似てますが、別物です。

BSONドキュメント(オブジェクト)は、生の状態(シリアライズド・イメージ)では配列なので、フィールドの検索は線形検索になり、決して速くはありません。検索を速くしたいなら、 バイトブロックからハッシュ構造を構築しなきゃならないので、これはこれでコストがかかります。

BSONドキュメントにたくさんのフィールド(プロパティ)を設けてハッシュのように使おうってのはたぶん良くなくて、コレクションとインデックスでハッシュを実現すべきだろうと思います。そのとき、ハッシュのバリューはBSONドキュメント。値の粒度はでかいです。

BSONの要素(オブジェクトのプロパティと配列の項目のこと)は、先頭にバイトサイズとタイプタグを持ってます。が、配列の項目数やオブジェクトのプロパティ数は持ってないので、長さを求めるのは苦手です。シーケンス(オブジェクトも配列もシーケンス)を先頭からスキャンして勘定するしかありません。シーケンス要素は不定長なのでバイトオフセットによる直接アクセスは不可能で、順番にたどっていきます。

現状(2010-11-25)だと、問い合わせ言語から配列の長さは指定できますが、ドキュメントのフィールド個数(オブジェクトのプロパティ個数)を指定したり取得する手段はありません。

BSONとJSONの違い

BSONを「JSONのバイナリーフォーマット」と思うと大間違い。データモデルが違います。

まず、異なるクライアントから保存された {"a": 1} と {"a": 1} が等しい保証はありません。「整数1」に対して、int32, int64, doubleの3種の表現があり、どれで保存されているかは状況次第*1。BSONにあわせるなら、1, 1L, 1.0 のような表記上の区別が必要です。

整数の曖昧さをクリアしたとしても、{"a":1, "b":2} と {"b":2, "a":1} は等しくありません。これは、明白に違うデータです。なぜなら、BSONのオブジェクトはフィールド(プロパティ)のシーケンスだからです。

現状では、フィールドの出現順序を無視したオブジェクト等値性を判断する手段はありません。したがって、JSONの意味での等号はBSONネイティブ*2では実現できません。いったん、シリアライズド・イメージからハッシュマップ(辞書)を構築して、そのデータ構造の等値性をプログラミング言語で調べることになるでしょう。オブジェクトのフィールド個数が取れれば、BSONネイティブになんとか等値性が判定できるので、いずれは改善するかも。

問い合わせ言語とストアードJavaScript

MongoDBの問い合わせ言語はrichだとうたってますが、僕の感覚ではプアな言語です。

「簡単なことは異常に簡単にできる」のがメリットですが、少し複雑なことはハマルかまったく出来ないか。機能性が乏しいのはマー許せるのですが、一様性、対称性、再帰性がなく、よくわかんない例外と制限があるくずれた言語です。Caty問い合わせ言語と「同じアイディアに基づき、構文もほとんど同じ」と言った部分は「簡単なことを簡単にできる」部分で、身贔屓じゃなくてやっぱりCatyクエリ言語のほうが(言語としては)デキがいいと思います。

それでもMongoDB問い合わせが実用に耐えるだろうと思えるのは、サーバーサイドのJavaScriptが使えるからです。そもそもが、「複雑な条件式はJavaScriptで書いてくれ」ってのが前提なんでしょう。スカラーに対する条件をJavaScriptで補完すると、曲がりなりにもboolean-closedな(AND, OR, NOTで閉じた)論理体系が作れるようです。

高水準な問い合わせ言語からMongoDB問い合わせ言語へのコンパイルを考える際は、JavaScriptコードの利用も考慮しないと苦しいでしょう。なお、JavaScriptエンジンには、SpiderMonkey以外にV8も使えるようです(詳細未確認だが)。

無理して使ってはいけない

ジョインやトランザクションは最初から非サポートと決めてかかっている機能なので、そういう機能が必要な目的にMongoDBは使えません。プログラムでジョインのようなことをしたり、一時コレクションを使って変更を保留することはできますが、そんなことを頑張るくらいなら、RDBを使ったほうがいいでしょう。

MongoDBが想定している用途は、例えば、ブログエントリーをメタデータも本文も画像もコメントも全部一緒に1ドキュメントとして、コレクションにぼんぼん放り込んでいくような使い方です。こういう用途に最適化された機能も付いています(例:カテゴリータグによる検索)。

「多機能でなんにでも使える」ようなプロモーションをしてますが、宣伝文句なんで文字通りに信用すべきじゃないですね、ってあたりまえだけど。

*1:ひとつの解決方法は、すべての数値をdoubleで保存することです。MongoDB付属の標準シェルはそうしています。

*2:JavaScriptコードを使わずに、という程度の意味です。