FAQ

ここでは、CouchDB に関するさまざまな質問とその回答をまとめています。

CouchDB について

CouchDB の使用について

ビューについて

セキュリティについて

システムにインストール済みの CouchDB について

この FAQ にまだ記載されていない質問があるときは、編集ボタンをクリックし、最後の部分に質問を追加してください。数日してからこのページにアクセスすれば、誰かが回答してくれているでしょう。

CouchDB とは?

CouchDB は、ドキュメント指向の非リレーショナル・データベース管理システム (Non-Relational Database Management System: NRDBMS) です。CouchDB システムの詳細については、「[WWW] はじめに」と「[WWW] 概要」を参照してください。

CouchDB は実用段階に達していますか?

アルファ・リリースの段階です。CouchDB はまだバージョン 1.0 をリリースしていません。おそらく現在のリリースから 1.0 のリリースまでの間に、データ・ストレージ形式の変更、さらに HTTP API についても互換性のない変更があるでしょう。ただし、さまざまな環境で CouchDB を使って成功を収めているプロジェクトも数多くあります。こうしたプロジェクトをすべてを網羅しているわけではありませんが、CouchDB を活用したプロジェクトのリストについては、「CouchDB の実際の活用例」を参照してください。

Couch の意味は?

Couch は、Cluster Of Unreliable Commodity Hardware(信頼性のないごくふつうのハードウェアのクラスタ)を意味する頭字語です。これは、障害の起きやすいハードウェア上で頑丈なスケーラビリティと高い信頼性を実現するという Couch の長期的な目標を表しています。CouchDB データベースの分散的な特質とフラットなアドレス空間により、(map/reduce スタイルのクエリー機能を備えた)ストレージ・スケーラビリティを実現するノード・パーティショニングと、信頼性とフォールト・トレランスを実現するクラスタリングが可能になります。

CouchDB は何という言語で記述されていますか?

耐障害性を重視した関数型並行プログラミング言語である Erlang で記述されています。初期の開発では C++ が使われましたが、その後、C++ に代わって Erlang OTP プラットフォームが使われています。これまでのところ、Erlang は CouchDB プロジェクトにうってつけの言語であることがわかっています。

CouchDB のデフォルト・ビュー・サーバーでは、Mozilla の Spidermonkey JavsScript ライブラリを使っています。このライブラリは C で記述されています。CouchDB では、ほかの言語で書かれたさまざまなビュー・サーバーも、簡単に組み合わせて使うことができます。

サポートされているプラットフォームは?

GNU/Linux と OS X を含むほとんどの POSIX システムをサポートしています。

Windows は公式にはサポートされていませんが、Windows でも動作するはずです。実際に試したら、動作の可否を知らせてください。

ライセンスはどうなっていますか?

[WWW] Apache 2.0 です。

CouchDB にはどれだけのデータを格納できますか?

ノード・パーティショニングを使えば、事実上無限です。単一のデータベース・インタンスでは、実質的な限界はまだわかっていません。

どうすればシーケンスを使えますか?

いわゆる AUTO_INCREMENT はないのですか、という質問ですね。レプリケーションを行う場合、シーケンスを実現するのは困難です。シーケンスは、しばしばあるデータベース・テーブル内の各行を一意に識別する ID として使われます。CouchDB では独自に一意の ID を生成し、ユーザーも独自の ID を指定することができます。したがって、CouchDB では必ずしもシーケンスは必要ありません。何らかの目的でシーケンスを使用する場合、CouchDB では別のやり方でシーケンスを表現する方法が見つかるでしょう。

レプリケーションを使うにはどうすればいいですか?

POST /_replicate

このとき、POST する内容は次のようにします。

{"source":"$source_database","target":"$target_database"}

$source_database と $target_database には、ローカル・データベースの名前もリモート・データベースのフル URI も指定できます。レプリケーション元とレプリケーション先のどちらのデータベースも、レプリケーションを実行する前に作成しておく必要があります。

レプリケーション中にどのような衝突が発生したかはどうやって調べればよいですか?

次のようなビューを使います。

map: function(doc) {if(doc._conflicts){emit(null,null);}}

複数のノード間で負荷分散を行うにはどうすればよいですか?

nginx のような HTTP プロキシを使えば、ノード間で GET 操作のロードバランシングを実現し、すべての POST、PUT、DELETE 操作をマスターノードで受け取ることができます。CouchDB のトリガによるレプリケーション機能では、複数の読み取り専用サーバーを 1 つのマスターサーバーと同期させることができます。したがって、マスターからスレーブへ定期的にレプリケートすることによって、コンテンツを最新の状態に保つことができます。

CouchDB はなぜ Mnesia を使わないのですか?

理由はたくさんあります。

HTTP API を使わずに CouchDB を操作できますか?

CouchDB のデータ・モデルと内部 API は、REST/HTTP モデルをほとんどそっくり写した形になっているので、ほかの API を用意するとしても、HTTP に似たものを再発明する結果になるでしょう。ただし、文書化されている Erlang API を提供できるように、CouchDB の内部構造をリファクタリングする計画はあります。

Erlang はなかなか Unicode を採用しませんでした。CouchDB を使ううえで、Unicode や UTF-8 が問題になることはありますか?

CouchDB は内部で Erlang バイナリを使っています。CouchDB に渡されるすべてのデータは、UTF-8 にエンコードする必要があります。

CouchDB でトランザクションを実行するにはどうすればよいですか?

CouchDB では、「オプティミスティックな並列性」モデルを採用しています。もっとかみくだいて言えば、ユーザーがあるドキュメント・バージョンを更新操作で送信した場合、送信されたドキュメント・バージョンと現在のドキュメント・バージョンが一致しなければ、CouchDB は変更を拒否する、という意味です。

一見シンプルですが、実際そのとおりシンプルです。通常のトランザクションを使用した多くのシナリオは CouchDB 用に書き換えることができます。ただし、CouchDB について学ぶときは、RDBMS の知識をすべて投げ捨てるつもりで取り組む必要があります。Couch を SQL ベースの世界にあてはめて理解するのではなく、もっと高い視点で問題を捉えるようにする方が望ましいといえます。

在庫の追跡

お尋ねの問題は、まず第一に在庫の問題です。アイテムについて説明したドキュメントがあり、このドキュメントに「在庫数」というフィールドが含まれているとすると、並列性の問題は次のようにして処理できます。

1. ドキュメントを取得し、CouchDB が返した _rev プロパティを記録します。2. 「在庫数」フィールドの値が 0 より大きければ、値をデクリメントします。3. _rev プロパティを使って、更新したドキュメントを送信します。4. _rev の値が現在格納されている値と一致すれば処理完了です。5. 衝突が発生した場合 (_rev の値が一致しない場合)には、最新のドキュメント・バージョンを取得します。さて、この場合、失敗する可能性のあるシナリオとして考慮しなければならないのは 2 つです。もし最新のドキュメント・バージョンの「在庫数」が 0 の場合には、RDBMS を使っている場合と同様に処理をする、すなわち目的の商品は購入できないことをユーザーに通知することになります。一方、もし最新のドキュメント・バージョンの「在庫数」が 0 より大きい場合には、更新後のデータで同じ処理を繰り返し、最初に戻ればよいことになります。RDBMS の場合より少し手間が増えますし、更新時の衝突が頻繁に発生するとやや面倒です。

上に示した回答は、RDBMS で行う場合とほぼ同様に、CouchDB で目的の操作を行うと仮定した場合のものです。私ならこの問題はもう少し違った方法で解決するでしょう。

私なら、ディスクリプタのデータ(商品名、画像、説明、価格など)をすべて収めた "master product" ドキュメントをまず作成します。次に、各インスタンスごとに "inventory ticket" ドキュメントを作成して追加します。このドキュメントには、product_key フィールドと claimed_by フィールドを用意します。たとえば、あるモデルのハンマーを販売していて、販売できる数量が 20 個だとします。この場合、それぞれのハンマーを表す hammer-1、hammer-2 といったキーを持つドキュメントができることになります。

次に、販売できるハンマーのリストを取得するビューを作成します。reduce 関数では、「合計」が表示されるようにします。これらはほとんど即興で考えたものですが、ビューが実際にどんな働きをするか、わかってもらえると思います。

Map

function(doc) 
{ 
    if (doc.type == 'inventory_ticket' && doc.claimed_by == null ) { 
        emit(doc.product_key, { 'inventory_ticket' :doc.id, '_rev' : doc._rev }); 
    } 
}

この関数では、 product_key ごとに利用可能な "tickets" のリストを取得できます。ハンマーの購入希望者がいる場合には、この "tickets" のリストを取得し、(id と _rev を使って) 更新を繰り返し、うまく ticket を取得できたら処理を止めます(すでに以前要求された ticket を取得しようとすると、更新エラーが返されます)。

Reduce

function (keys, values, combine) {
    return values.length;
}

この reduce 関数は、まだ要求されていない inventory_ticket アイテムの合計数を返すので、販売できる「ハンマー」がいくつあるか知ることができます。

注意事項

このソリューションは、お尋ねの問題について 3 分半ほど考えて作成したものです。きっと、もっとうまいやり方があると思います。とは言っても、上のソリューションで更新時の衝突は大幅に減りますし、衝突が発生したときに新しく更新を行う必要性も少なくなります。このモデルでは、商品の大元のエントリーに含まれているデータを複数のユーザーが同時に変更しようとする状態は起きません。最悪の場合でも、単一の ticket を複数のユーザーが要求するだけで、すでにビューからいくつか ticket が存在することがわかっていれば、対象を次の ticket に移して、再度処理を試みればいいだけです。

(この FAQ の項目は、「[WWW] http://stackoverflow.com/questions/299723/can-i-do-transactions-and-locks-in-couchdb」から、記事を書いた本人の承諾を得て転載したものです。)

ビュー・インデックスを、ユーザーが読み取ったときだけではなく、もっと頻繁に更新するにはどうすればよいですか?

そのような形でビューの更新を行うには、「外部プロセス」で説明されているように、 CouchDB と一緒に実行するちょっとしたデーモン・スクリプトを作成し、これを couch.ini で指定します。このデーモンでは、データベースが変更されるたびに通知を受け取り、N 個のドキュメントが挿入されたとき、または Y 秒が経過したときのいずれか早い方が到来したときに、ビューの更新を実行させるようにします。ドキュメントが追加されるたびに処理を実行しないのは、この操作が非常に非効率だからです。CouchDB はビュー・インデックスの更新を高速に実行できるよう設計されているので、バッチ処理の方が適しています。例については、「更新時のビューの再作成」を参照してください。

データベースに含まれるすべてのビューのリストを取得するには、GET /db/_all_docs?startkey=_design/&endkey=_design/ZZZZ を実行します(なお、ZZZZ のようなハックをしなくてよいように、/db/_all_design_docs ビューを用意する予定です)。

これで、お尋ねの問題も解決されるはずです。

このようなデーモンは本来 CouchDB と一緒に配布すべきでしょうが、開発陣は残念ながらまだデプロイメント・インフラについて作業できる段階にはありません。この方面に関してもし手伝ってくれる人がいれば、大歓迎です。開発陣はヘルパースクリプトの言語として Python を考えていますが、どんな言語でも可能ですし、自分に合った言語を使ってもらえばと思います。

リモートサーバーで CouchDB を実行していますが、セキュリティ上、公開ポートで接続を受けることは避けたいと思っています。このような条件の下で、ローカルマシンからリモートサーバーに接続する方法はありますか? また、その場合に Futon を使えるようにできる方法はありますか?

ローカルマシン上でリモートサーバーへの SSH トンネルを作成し、ローカルマシンのポート 5984 へのリクエストをリモートサーバーのポート 5984 に転送するようにします。

$ ssh -L5984:127.0.0.1:5984 ssh.example.com

以後は、[WWW] http://localhost:5984/ でリモートサーバーの CouchDB にアクセスできるようになります。

CouchDB のビューはどれくらい高速ですか?

速度を表す数字を意味のある形で示すことは困難です。アーキテクチャの観点から言えば、テーブルのビューは、RDBMS で言うテーブルの(マルチカラム)インデックスに似ており、このインデックスでは高速な参照を実行できます。したがって、理論上は CouchDB のビューはきわめて高速だと言えます。

ただし、CouchDB のアーキテクチャの主な利点は、高いトラフィックに対応できるよう設計されていることです。ストレージ・モジュール内で (MVCC などにより) ロックが起きないことで、任意の数のユーザーが並行して読み取り可能なほか、シリアライズされた書き込みも可能です。レプリケーションを使えば、複数のマシンを用意して水平方向のスケールアウトが可能ですし、データ・パーティショニング(今後追加予定の機能)を使えば、膨大な量のデータにも対処できます(ストレージモジュールの詳細については、[WWW] Jan Lehnardt の記事のスライド 13 を参照してください。CouchDB 全体に関する詳細な情報については、記事の全体を参照してください)。

2) ビューの中で2 番目の引数に doc を取る emit() 関数を使えば、実質的にデータ全体をビュー・インデックスの中にコピーできます。ただ、この処理にはかなり時間がかかります。emit() の呼び出し方法を書き換えて emit(key, null); とし、?include_docs=true パラメータを指定してビューをクエリーすれば、データをビュー・インデックスにコピーしなくとも、ビューでドキュメントのデータすべてを取得することができます。

3) Erlang release R11B (または 5.5.x) を使用している場合は、少なくとも R12B-3 (または 5.6.3) にアップグレードしてください。

CouchDB でリレーションを作成するにはどうすればよいですか? JOIN に相当するものはないのですか?

[WWW] http://www.cmlenz.net/archives/2007/10/couchdb-joins を参照してください。

複数のビューでコードや関数を共有するにはどうすればよいですか? できないとすれば、それはなぜですか?

HTTP ビュー API」の中の「複数のビューでのコードの共有」を参照してください

CouchDB のログファイルはどこにありますか?

* Linux/UNIX にデフォルトでインストールした場合、ログファイルは次の場所にあります。

* ソースからインストールして開発モードで CouchDB を実行している場合、ログファイルは次の場所にあります。

last edited 2009-06-04 14:38:03 by FreddyBowen