5分でCouchDBを起動する — まず動かして、後から理解する
最初から理論を説明するつもりはない。まずインストールして動かしてみれば、なぜ他のDBと違うのかが自然とわかってくる。
Ubuntu/Debianへのインストール
# Apache CouchDBの公式GPGキーとリポジトリを追加する
curl -L https://couchdb.apache.org/repo/keys.asc | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/couchdb.gpg
echo "deb https://apache.jfrog.io/artifactory/couchdb-deb/ $(lsb_release -cs) main" | \
sudo tee /etc/apt/sources.list.d/couchdb.list
sudo apt update && sudo apt install -y couchdb
セットアップウィザードでモードを選択する:1ノードで動かすならstandalone、複数台構成ならclusteredを選ぶ。管理者パスワードはこのステップで設定すること — 後からやり直す機会はない。
サービスが起動していることを確認する:
sudo systemctl status couchdb
curl http://localhost:5984/
# 期待される出力:
# {"couchdb":"Welcome","version":"3.x.x", ...}
データベースとドキュメントの作成
# "myapp"という名前のデータベースを作成する
curl -X PUT http://admin:yourpassword@localhost:5984/myapp
# 最初のJSONドキュメントを追加する
curl -X POST http://admin:yourpassword@localhost:5984/myapp \
-H "Content-Type: application/json" \
-d '{"name": "Nguyen Van A", "role": "developer", "city": "Hanoi"}'
# データベース内の全ドキュメントを確認する
curl http://admin:yourpassword@localhost:5984/myapp/_all_docs?include_docs=true
http://localhost:5984/_utils にアクセスするとFauxtonが開く — 追加インストール不要の組み込みWeb UIだ。テスト中にドキュメントを手動で作成・確認・編集するのに非常に便利だ。
MySQL、PostgreSQL、MongoDBを使ってきた — CouchDBは何が違うのか?
複数のプロジェクトでこれら3つすべてを使ってきたが、それぞれに適した場面がある。MySQL/PostgreSQLはデータ構造が固定されていてリレーションが明確なときに最適だ。MongoDBはスキーマの柔軟性があるが、レプリケーションはprimary-secondaryが基本で — secondaryノードは読み取り専用で書き込みができない。
CouchDBが解決するのはまったく別の問題だ:ネットワークがなくてもアプリが動作し続け、接続が戻ったら同期するというケースだ。現場スタッフ向けアプリ、電波の弱いエリアのPOS、屋外の物流システム — こういった場面がCouchDBの本領だ。
CouchDBが実際に違うと感じる3つのポイント
- 純粋なHTTP API:すべての操作はRESTで行う。専用ドライバー不要 —
curlだけで始められる。 - MVCC(Multi-Version Concurrency Control):書き込み時にロックを使わない。各ドキュメントはリビジョンを追跡する
_revフィールドを持つ。読み取りが書き込みにブロックされないため、読み取りスループットが非常に高い。 - マルチマスターレプリケーション:すべてのノードに書き込みができる。同期時にCouchDBは自動でコンフリクトを検出 — どう解決するかはあなたが決める、データベースではなく。
CouchDBのドキュメントモデル
各ドキュメントは_id(自動生成または手動指定)と_rev(リビジョンハッシュ)を持つJSONだ:
{
"_id": "order_20260710_001",
"_rev": "1-abc123def456",
"customer": "Tran Thi B",
"items": [
{"sku": "LAP001", "qty": 2, "price": 15000000},
{"sku": "MOU002", "qty": 1, "price": 350000}
],
"total": 30350000,
"status": "pending",
"created_at": "2026-07-10T09:00:00Z"
}
更新するたびに_revが変わる。2つのノードがオフライン中に同じドキュメントを編集した場合、CouchDBは両方のバージョンを保持してconflictとしてマークする — マージはあなたが後で決める。
レプリケーションの設定 — CouchDBが最も得意とする部分
一方向レプリケーション(one-way)
# "myapp"データベースをサーバーAからサーバーBにコピーする
curl -X POST http://admin:pass@localhost:5984/_replicate \
-H "Content-Type: application/json" \
-d '{
"source": "http://admin:pass@server-a:5984/myapp",
"target": "http://admin:pass@server-b:5984/myapp",
"create_target": true
}'
継続的レプリケーション(Continuous Replication)
# "continuous": true でレプリケーションを継続実行し、1回で終了しないようにする
curl -X POST http://admin:pass@localhost:5984/_replicate \
-H "Content-Type: application/json" \
-d '{
"source": "http://admin:pass@server-a:5984/myapp",
"target": "http://admin:pass@server-b:5984/myapp",
"continuous": true,
"create_target": true
}'
双方向レプリケーション(Bidirectional Replication)
2つのジョブを並行実行する — A→BとB→Aの両方向:
# A → B 方向
curl -X POST http://admin:pass@server-a:5984/_replicate \
-H "Content-Type: application/json" \
-d '{"source": "myapp", "target": "http://admin:pass@server-b:5984/myapp", "continuous": true}'
# B → A 方向
curl -X POST http://admin:pass@server-b:5984/_replicate \
-H "Content-Type: application/json" \
-d '{"source": "myapp", "target": "http://admin:pass@server-a:5984/myapp", "continuous": true}'
設定が完了すると、変更があるたびに2台のサーバーが自動で同期する — 片方がオフラインから復帰した後も含めて。追加の設定は不要だ。
同期後のコンフリクト処理
コンフリクトは思っているよりも頻繁に発生する — 特に従業員が3Gの不安定なエリアでアプリを使い、2時間オフラインになってから同期する場合に。CouchDBはコンテンツを自動でマージしない — どのリビジョンを採用するかはあなたが決める。
import requests
base = "http://admin:yourpassword@localhost:5984"
# コンフリクトが発生しているドキュメントを検索する
def find_conflicts(db):
url = f"{base}/{db}/_all_docs?include_docs=true&conflicts=true"
docs = requests.get(url).json()["rows"]
return [row["doc"] for row in docs if "_conflicts" in row.get("doc", {})]
# コンフリクトしているリビジョンを取得する
def get_conflict_revs(db, doc_id, conflict_revs):
results = []
for rev in conflict_revs:
url = f"{base}/{db}/{doc_id}?rev={rev}"
results.append(requests.get(url).json())
return results
# 負けたリビジョンを削除する(残したいリビジョンを保持する)
def resolve_conflict(db, doc_id, losing_rev):
url = f"{base}/{db}/{doc_id}?rev={losing_rev}"
resp = requests.delete(url)
print(f"Deleted losing rev: {resp.status_code}")
# 使用例
conflicts = find_conflicts("myapp")
for doc in conflicts:
print(f"Conflict on: {doc['_id']}")
revs = get_conflict_revs("myapp", doc["_id"], doc["_conflicts"])
# 現在のリビジョン(winning)を保持し、コンフリクトしているリビジョンを削除する
for rev in doc["_conflicts"]:
resolve_conflict("myapp", doc["_id"], rev)
マージのロジックはビジネスロジックに依存する:注文データならcreated_atで最新を採用し、アクティビティログなら両方を保持する必要がある。CouchDBは仕組みを提供する — ルールはあなたが書く。
CouchDBを実際に使う前に知っておくべき4つのこと
1. クライアント側でPouchDBと組み合わせる — 完全なオフラインファーストのコンビ
PouchDBはブラウザやReact Nativeで直接動作するJavaScriptライブラリ(~46KB gzip圧縮後)だ。APIはCouchDBに似ており、ネットワークが復帰すると自動で双方向同期する:
// Webアプリ/React Nativeアプリ内
const db = new PouchDB('local_myapp');
const remoteDB = new PouchDB('http://admin:pass@yourserver:5984/myapp');
// 双方向同期を有効にし、ネットワーク復帰時に自動再接続する
db.sync(remoteDB, { live: true, retry: true })
.on('change', info => console.log('Synced:', info))
.on('error', err => console.error('Sync error:', err));
ユーザーはオフラインのまま普通に入力できる。ネットワークが戻ると、PouchDBが自動でプッシュとプルを行う — 追加のコードは不要だ。
2. インデックスを作成してクエリを高速化する
デフォルトのCouchDBはビュー(MapReduce)を使う — 書くのが面倒だ。CouchDB 2.x以降はMango Queryが使えて、はるかに簡単だ:
# "status"と"created_at"フィールドにインデックスを作成する
curl -X POST http://admin:pass@localhost:5984/myapp/_index \
-H "Content-Type: application/json" \
-d '{"index": {"fields": ["status", "created_at"]}, "name": "status-index"}'
# Mango構文でクエリを実行する
curl -X POST http://admin:pass@localhost:5984/myapp/_find \
-H "Content-Type: application/json" \
-d '{
"selector": {"status": "pending"},
"sort": [{"created_at": "desc"}],
"limit": 20
}'
3. CouchDBを使わない方がいいケースは?
- 複数テーブルの複雑なJOINが必要な場合 → PostgreSQLを使う。
- 時系列データやメトリクス → TimescaleDBかInfluxDBを使う。
- 高速キャッシュ → Redisを使う。
- CouchDBが適しているのは:ドキュメント型データ、オフラインファーストが必要、分散ロックを気にせずマルチマスターが必要な場合。
4. ブラウザからPouchDBを使う場合はCORSを有効にする
curl -X PUT http://admin:pass@localhost:5984/_node/nonode@nohost/_config/httpd/enable_cors \
-d '"true"'
curl -X PUT http://admin:pass@localhost:5984/_node/nonode@nohost/_config/cors/origins \
-d '"*"'
curl -X PUT http://admin:pass@localhost:5984/_node/nonode@nohost/_config/cors/methods \
-d '"GET,PUT,POST,HEAD,DELETE"'
本番環境では"*"を特定のドメインに変更すること。開発中は"*"のままで便利だが、実際のデプロイ時はそのままにしないこと。
CouchDBはすべての問題に対応するデータベースではない。しかし、アプリがオフライン時でも動作して後で同期する必要がある場合、または複数のノードが同時に書き込む必要があって分散ロックに悩みたくない場合 — それをワークアラウンドなしでそのまま解決してくれる。

