ひょっとして、Ajax をうまく使って Google Bigtable みたいなフレキシブルDBシステムを作れるんじゃね?
今日、会社でWebサイトのユーザー特性についての情報共有やってて、もっと正確なトラッキングができる方法ないかな? と思った。
ちょうど、Google の Webサービス(Google App Engine)が一般公開されることになったのも一つのきっかけだったけど、Bigtable は使えるんじゃないか、と。
そうはいっても、いきなり Google App Engine に参入するのは、会社として決断できるはずもない。だから、現在運用しているサーバに Bigtable のようなフレキシブルなDBシステムを構築できないかと考えた。また、専任でプログラマーを常駐させていないこともあり、サーバーサイドのプログラミング作業は最小限に抑えたいというのもあった(コストの節約)。
正直、私が書いちゃってもいいんだけど、サーバー側のプログラムいじる権限がないのでね。
で、Ajax を使う方法を思いついた。
1.(まず前提として)Bigtableとは?
Bigtable とは、Google が従来のRDBでは扱いきれないほど大きなデータを読み書きできるデータベースシステムで、行だけでなくコラム(列)も自由に拡張できるフレキシブルなもの。主に「行」「コラム」「タイムスタンプ」で構成される。すなわち、行キー+コラムキー+タイムスタンプで一つのキーが割り当てられ、値が格納される。この場合の値は、BLOB型と言われる構造データでバイナリデータを格納できるのが特長である。またRDBと比べてHDDへのアクセスが少ないため、データを高速処理するのも大きな長所となっているようだ。
逆に、欠点としては、RDBと比べてデータの格納効率が悪くなることだろうか。また、SQLコマンドを使うことに慣れ親しんだプログラマーにとってはなじまない方法になるかもしれない。Google App Engine (β版)をリリースするに当たって、Google はGQL(ベタやな〜)と言われる専用言語を用意しているので多少はましなのかな?
2. Bigtable の構造を、Webサーバのファイルシステムに置き換える
Bigtable のデータは、Google のサーバ内の独自ファイルシステム(GFS)に格納してある。GFSについて説明するとかなり複雑なので、ここでは割愛するが、とりあえず、このシステムを通常のWebサーバで再現することは不可能に近いと思う。そこで、Webサーバのファイルシステムに擬似的に Bigtable を格納する方法を考えてみる。と言っても、すごく単純な方法。
Bigtable のキーは「行キー+コラムキー+タイムスタンプ」なので、これを単純にファイル名にしてしまうのもいいけど、せっかくなので、ディレクトリ構造に割り当ててみたい。すなわち、格納ディレクトリの下に「行キー」ディレクトリを作成し、さらにその下に「コラムキー」ディレクトリを置き、「タイムスタンプ」ファイルを作る。拡張子は何でもいいんだけど、わかりやすいように“.dat”にしとこう。
こんな感じになる。→[行キー]/[コラムキー]/[タイムスタンプ].dat
本物の Bigtable はバイナリデータを格納できるが、ここでは最終的に JavaScript で扱うことが前提なので、JSONやXML、(X)HTML、カンマ区切りテキスト、プレーンテキストなどが望ましい(JSON or プレーンテキストが一番楽と思う)。
3. サーバーサイドプログラムをつくる
サーバーサイドプログラムは、後で修正などしなくてもいいように、汎用性の高いものを作っておく。本物の Bigtable のコマンドが、Set(追加)、Delete(削除)、Lookup(検索) なので、ここでもこの3つそろえておけば十分足りると思う。本物の Bigtable の場合は、トランザクション処理と書き込みエラーの対策として、別途 Apply コマンドが送られたときに初めて処理が実行される。仮にトラッキングで使うとしたら、トランザクションは要らなそうだし、タイムスタンプ残しながら追記していくことになるので、書き込みエラーも考えにくい(エラーになっちゃったら書き込もうが書き込むまいが同じこと)。
と考えると、Apply コマンドは無くても良さそうだけど、どこで使うことになるかわからないし、Google が経験上必要と言っているので、つけといた方がいいかもしれない。
各コマンドの内容は
Set →
【入力】[行キー](そのレコード固有のID)+[コラムキー](コラム名=レコードの項目名)+[タイムスタンプ](ファイルを書き込んだ時間)+値([タイムスタンプ]に書き込む内容)
【出力】書き込みの成否
レコードを更新する場合は、
- “[行キー]/[コラムキー]”ディレクトリが存在するかどうか確認。無い場合は作成
- 値を取得。
- すでに“[タイムスタンプ].dat”のファイルがある場合は“[タイムスタンプ].bak”などにリネーム
- “[タイムスタンプ].dat”を作成し、値をプリント&保存
- 4.まで成功した場合は、“真”を出力し、“[タイムスタンプ].bak”を削除。失敗した場合は“偽”を出力し、“[タイムスタンプ].bak”を“[タイムスタンプ].dat”に戻す。
Delete →
【入力】[行キー]、[コラムキー]、[タイムスタンプ]の任意な組み合わせ
レコード全体を消したい場合は、[行キー]のみ。全てのタイムスタンプにおけるコラムを消したい場合は、[行キー]+[コラムキー]、一つのタイムスタンプだけレコードを消したい場合は、[行キー]+[タイムスタンプ]という具合に組み合わせれば、(疑似)データベース内のデータを自由に削除することができます。
【出力】書き込みの成否
Lookup →
【入力】[行キー]、[コラムキー]、[タイムスタンプ]の任意な組み合わせ
【出力】検索結果を配列で出力(と思ったけど JavaScript で使うから、eval関数で配列に変換可能なテキスト)
Apply → 上の3つのコマンドを実行
4. Ajaxを組み込む
というか xmlHttpRequest で非同期通信する。
send関数で[行キー]、[コラムキー]、[値]、それから、実行コマンドを指定してサーバーに送る。
新規追加の場合、重複を避けるため[行キー]はサーバーサイドで発行する。その際、例えばトラッキングのようにユーザーが認知しない形でのデータを取得したい場合は、認証のために[行キー]を Cookie に残しておく。
(詳細は割愛)
Ajaxでのデータ通信は、セキュリティ的にやばくなる場合がままあるので、細心の注意を払う。
5. セキュリティ
6. トラッキングに特化した場合
使用目的をトラッキングに特化した場合(個人情報を扱わない場合に限る)は、セキュリティなどをあまり考えなくてすむかもしれない。例えば、“[タイムスタンプ].dat”をWebサーバのルートディレクトリ以下に格納すれば、(サーバーサイドプログラムを介さず)xmlHttpRequest で直接読み込むことも可能になる。データファイルは外部から丸見えだけどね。この場合、JavaScriptにはファイルシステム関数がないので、マスター情報を記録するデータファイルを別途用意する必要があるかもしれない。
なんだかややこしそうですが、3のサーバーサイドプログラムさえ作り込んでおけば、あとはクライアント側の JavaScript をコーディングするだけで自在にデータベースを作り替えてしまうことができます。サーバーサイドの運営を外部業者に委託するなどの理由で、コストパフォーマンスが悪かったり、スピーディな運用が難しかったりする場合は、かなり効果的ではないかと思います。
サーバー負荷がいまいち読めませんが、トラッキングやその結果を反映したパーソナライズドページの生成やセグメント広告の展開とかだったら、検索する行キー・コラムキーが明確なので、CPU負荷もメモリ負荷も最小限で収まるはず。フリーワード検索とか行うには、もう一工夫必要になりますが。
ちょっと荒いですかね?