Upload
yohei-sasaki
View
3.195
Download
2
Embed Size (px)
DESCRIPTION
CouchDB introduction at BPStudy.
Citation preview
2010/04/23
id: yssk22 (CouchDB-JP)
自己紹介
Yohei Sasaki (yssk22)
developerWorks のCouchDB連載記事
CouchDB基礎文法最速マスター
CouchDB-JP
People on the Couch: UTC+9
今年から某ECサイトの開発
はじめに / お願い
質問は随時... というのは大変なのでハッシュタグ #bpstudyを添えてつぶやいてください。
つぶやきに 半角?または全角? をいれていただければ回答します。
ハンズオン用URL
○ http://bit.ly/6rXbn5
目次
CouchDBとは何か?
HTTP API
MapReduce
CouchDB Application
まずは思想的な話から...
CouchDBとは何か?
KVSっぽいデータストア
Apache プロジェクト
HTTP によるアクセス
MapReduceでクエリができる
Erlangで実装されたデータベース
Lotus Notes みたいなレプリケーション
すべて正しいけれど、その前にもっと本質的な話を...
CouchDBの思想の原点: Relax
CouchDB: The Definitive Guide より... If there’s one phrase to describe CouchDB it is relax.
making CouchDB easy, event a pleasure, to use.
当たり前のことが当たり前に感じられる道具であること。
開発するときの当たり前感
○ ドキュメント指向
運用するときの当たり前感
○ いつでも利用可能
データモデルに対するアプローチ Webそのもの。 WWW はデータベース
WWW はドキュメント指向データベース
Django may be built for the Web, but CouchDB is built of the Web.
- Jacob Kaplan-Moss, Django Developer
Document Oriented Database Webで実現する文書そのものを格納するためのデータベース。
例:請求書
請求元: hogehoge株式会社請求先:
郵便番号: 123-4567
住所: 神奈川県川崎市...
氏名: 佐々木庸平
請求金額: \12,345
明細:
商品数量金額小計AAAA 1 1,980 1,980
BBBB 3 1,210 3,630
...
CouchDBにおける1つの保存単位- ドキュメント
例:請求書 (RDBの場合)
1, hogehoge株式会社
3, 123-4567, 神奈川県..., 佐々木庸平
10, 1, 1
10, 2, 31, AAAA, 1,980
2, BBBB, 1,210
請求元: 1
請求先: 3
請求金額: SUM(小計)
明細商品数量金額小計1 1 1.P 1 * 1.P
2 3 2.P 3 * 2.P
正規化!正規化!正規化!
正規化とドキュメント指向の狭間 正規化の利点はデータの整合性を保つこと。 例えば、請求書の発行後に住所が変わったら? 顧客マスタ上の住所を変更する あれ? 昔の請求書の住所までかわっちゃったんだけど...
現実世界の"ドキュメント"は整合性を保たないほうがよい場合も。 現実世界のとおりであることが当たり前 = Relax
Web を作るイメージでデザインするとしっくりくる。
1 URI = 1 ドキュメント
でも、マスタ管理はしたい。
ドキュメント指向的アプローチ請求元: hogehoge株式会社請求先:
郵便番号: 123-4567
住所: 神奈川県川崎市...
氏名: 佐々木庸平
請求金額: \12,345
明細:
商品数量金額小計AAAA 1 1,980 1,980
BBBB 3 1,210 3,630
...
hogehoge株式会社
佐々木庸平
AAAA, 1,980
BBBB, 1,210
台帳から書き写す、という事務処理
データモデルを定義するタイミング RDB
最初に蓄積すべきデータをモデル化しておく○ 業務分析
○ エンティティを切り出し
○ 正規化
○ 業務の決まりを事前定義
CouchDB
随時やる
業務の決まりを随時定義 現実世界で随時やるように...
スケールに対する考え方
CouchDBにとっての基幹システム ユーザーのブラウザ
Webのだめなところ レイテンシー ユーザーネットワークはSPoF
スケールダウンしても利用可能なサービスを提供する アプリケーションとデータ(のサブセット)を丸ごとユーザーに複製させる
通勤経路の稼働率
稼働率 24min/30min : 80%
具体的なサービスの例: gmail, google reader, twitter, hatena.ne.jp, ...
サービス提供側としては100%を目指しているのを十分承知しているものの...
レプリケーションによる可用性の確保ができるかもしれない
App
App
1台になっても、N台になっても対応できるストレージシステム
HTTP
ここまでのまとめ
CouchDB = Relax
当たり前のことを当たり前に。
データモデルに対するアプローチ
○ ドキュメント指向
スケールに対するアプローチ
○ スケールダウン
○ レプリケーション
実際の使い方
CouchDBの使い方
HTTP で使う 使い方 = Web で当たり前の使い方
○ GET : ドキュメントを取得する
○ POST : ドキュメントを作成する
○ PUT : ドキュメントを更新する
○ DELETE : ドキュメントを削除する
Web の常識をしっていれば、どの言語/ランタイムでもCouchDBは簡単○ 「Webを支える技術」参照
ドキュメントの表現方法
JSONで記述
すべてのドキュメントは一意に識別するためのIDとバージョン番号REVをもつ
他は自由
{
"_id" : "請求番号XXXXYYYY", "_rev" : "3-ZZZZZMMMM"
"発行者": "hogehoge株式会社"
"請求先": {
"郵便番号": 123-4567"
"住所": "神奈川県川崎市..."
"氏名": "佐々木庸平"
},
"請求金額": 12345
"明細": [
{ "商品: "AAAA", "数量": 1, "単価": 1980, "小計": 1980 },
...
]
}
Futon による確認
GUI Tool
http://localhost:5984/_utils/
Firefox + Firebug を使うのがbetter
デモ
補足: リビジョン管理
CouchDBはMVCCによりディスクアクセスを行う データの更新操作(PUT)は、論理更新であり物理的には追加が行われる。
_rev には"更新回数-ドキュメントのMD5" が格納されている
_rev を使った Optimistic Lock により更新の衝突を検出する
Compaction という操作により、古いリビジョンは削除される
HTTP API 一覧
後でリファレンス的に利用できるようにSphinx Document にまとめみました http://bit.ly/b008oP
HTTPメソッドは省略 慣習に従えばよいので。
○ XXを実行する 系はPOST
ハンズオンで curl を使って試します。
HTTP Header も活用しよう
Request X-Couch-Full-Commit
○ true
レスポンス前に確実にディスクに書き込む
- 帳票などのドキュメントに使う
○ false
ディスクに書き込む前に HTTP 201 を返す
- ポストイット的なドキュメントに使う
Response ETag
○ ドキュメントの _rev と同じ値が入る JSONのparseをしなくても_revを入手できる
そのほかの主要機能
レプリケーション 二つのデータベースの同期をとる
○ P2P モデルで、双方向に同期をとることができる。 半自動実行なので、ネットワークが切れても問題がない。
添付ファイル ドキュメントにはバイナリファイルを添付できる。○ メディア+メタデータ管理とかに利用可能
各種言語からの利用
HTTPライブラリとJSONライブラリが必要十分条件
以下独断と偏見によるおすすめ JavaScript
○ /_utils/script/jquery.couch.jsが標準添付
Ruby○ couchrest
Python○ couchdb-kit
○ python-couchdb
Java○ couchdb4j
データ処理のための手法...
デザインドキュメント
デザインドキュメント
CouchDBに格納されるアプリケーションを定義したドキュメント
ビュー
デザインドキュメントの中に定義するMapReduce関数を記述し、インデックスを作成する機能
デザインドキュメントの構成 {
"_id" : "_design/app",
"_rev" : "3-XXXXXX",
"language" : "text/javascript",
"views" : {
"count_by_name" : {
"map" : "function(doc){ ... }",
"reduce" : "function(k,v,r){ ... }"
}
},
"shows" : ...
"lists" : ...
}これ
ビューの用途
基本 ドキュメントのフィルタリング 並び替え 内部的にはインデックスを作る作業
○ ドキュメントを効率的に見つけるために!
様々な計算○ SUM とか。
応用 (データ構造の)フォーマット
○ "正規化", "非正規化" も含めて
覚えておくべきこと: クエリではありません。
○ "クエリ"の機能 ... URLにクエリ文字列っていうものがありますよね?
MapReduce
Map
1つのドキュメントから別のデータを作成する。
○ 別のデータ : Key-Value の組み合わせ
Reduce
Keyが同じデータの集合から、データを集約して1つの値を計算する。
○ reduce フェーズと rereduce フェーズがある
サンプルデータ
http://search.twitter.com/?q=%23bpstudy
{
"_id": "479ce1c32f79d7864730ecbae60d6610",
"_rev": "1-f45bf5f71b84692d0acc4b196849c2d7",
"iso_language_code": "ja",
"type": "tweets",
"keyword": "#bpstudy",
"text": "1時間遅れたけど終わった。これであとは資料の残り半分を完成させればOK
#bpstudy (といってハッシュタグのテスト)",
"created_at": "Sat, 17 Apr 2010 19:07:16 +0000",
"profile_image_url":
"http://a3.twimg.com/profile_images/427387065/speedland_normal.png",
"source": "<ahref="http://sites.google.com/site/yorufukurou/"
rel="nofollow">YoruFukurou</a>",
"from_user": "yssk22",
"from_user_id": 3749810,
"to_user_id": null, "geo": null, "id": 12356675615,
"metadata": { "result_type": "recent" }
}
ハンズオンでやります。
map 関数 の書き方
function(doc){
emit(key, value);
emit(key, value);
...
}
emit(key, value)
発言者ごとにemitする
function(doc){function format_date() { ... }; // 省略if( doc.type == ''tweets'' ){
var t = format_date(t);emit([doc.from_user, t],
null);}
}
emit(key, value) で出力を定義 key はobject以外の任意の値 value は null でもよい。
○ undefined はJSONの仕様外なのでだめ
結果
/{db}/_design/{app} /_view/{viewname}
で確認可能
{"total_rows":169,"offset":0,"rows":[
{"id":"...","key":["AE35","2010/04/02 01:36"],"value":null},
{"id":"...","key":["albuk","2010/04/01 15:36"],"value":null},
{"id":"...","key":["ali_lin5757","2010/04/05 20:02"],"value":null},
{"id":"...","key":["Amoreeeee","2010/04/06 15:45"],"value":null},
{"id":"...","key":["aodag","2010/04/02 01:57"],"value":null},
...
]}
ビューに対してクエリをかける よく使うのは範囲指定 startkey=xxx&endkey=yyyy
特定の発言者だけ時系列にとりだす startkey=["yssk22"]&
endkey=["yssk22", "\u9999"]
○ ["yssk22"] より大きくて、["yssk22", "\u9999"] より小さいキーを抽出 配列は要素ごとに比較して順序づけ
文字列の最大値は \u9999
CouchDBのMapReduceの特徴 B+Tree構造でディスクに結果を保持
インクリメンタルに実行される
一度計算した結果は、元のドキュメントが更新されない限りキャッシュが保持される。
CouchDBのMapReduceの特徴 B+Tree構造でディスクに結果を保持
インクリメンタルに実行される
一度計算した結果は、元のドキュメントが更新されない限りキャッシュが保持される。
オンラインシステムで使えるMapReduceの仕組み
再計算しない 再計算しない再計算する再計算しない
更新
発言数をカウントする
map 関数の定義
function(doc){
function format_date() { ... }; // 省略if( doc.type == ''tweets'' ){
var t = format_date(t);
emit([doc.from_user, t],
1);
}
}
発言数をカウントする
reduce 関数の定義
function(keys, values, rereduce){
if( rereduce ){
return sum(values); // sum([2,5,3,4, ...])
}else{
return values.length; // [1,1,1,1,...].length
}
}
rereduce に注意
rereduce()
reduce()
MapReduceの実行モデル
Rereduceに注意
これは正しく動かない場合がある function(key, values, rereduce){
return values.length;}
これは正しく動くが遅い場合がある function(key, values, rereduce){
return sum(values);}
reduce 結果のデータ構造は意識して計算を記述する
アプリケーションとデータベースの奇妙な関係
ちょっと前のWeb+DB
Web Server Web Server Web Server Web Server
App Server App Server App Server App Server
RDB Server RDB Server
最近の Web+DB
Web Server Web Server Web Server Web Server
App Server App Server App Server App Server
DB Server DB Server DB Server DB Server
RDBだったりKVSだったり...
CouchDB = 1人で3役
Relax Relax Relax Relax
CouchDBはWebを作るデータベース Webを作るのに必要な機能を一通りもっている。
HTTPリクエストに対して、以下の振る舞いを提供する
静的なデータを返す
HTTP API
動的なデータを返す
Show, List 関数
適切な振り分けを行う
Virtual Host, URL Rewrite
適切にデータを更新する
HTTP API, Update 関数, Validation 関数
再びデザインドキュメント
{
"_id" : "_design/app",
"_rev" : "3-XXXXXX",
"language" : "text/javascript",
"views" : ...
"shows" : {
"tweet": "function(doc, req){ ... }"
}
"lists" : {
"timeline": "function(head, req){...}"
}
}
これ
Show関数
ドキュメントを表示する. /{db}/_design/{app} /_show/{showname}/{doc_id}
○ doc_idは Optional
JavaScript で記述 ... !?
Server Side JavaScript.
tweet の表示
function(doc, req){
var t = require('vendor/crayon/lib/template');if(doc &&doc.type == 'tweets'){
bindings["page.title"] = "@" + doc.from_user + ": " + doc.text;
bindings["tweet.id"] = doc.id;
bindings["tweet.text"] = doc.text;
...
returnt.render(ddoc.templates.site.header, bindings) +
t.render(ddoc.templates.tweet, bindings) +
t.render(ddoc.templates.site.footer, bindings);
}
...
URLからドキュメントを変数にマップ
3rd party ライブラリの利用
コンテンツを文字列で返す
List関数
Viewの結果を表示する. /{db}/_design/{app} /_list/{listname}/{viewname}
○ viewnameは 必須
○ クエリ文字列も利用可能
例によってJavaScript で記述
Server Side JavaScript.
timeline の表示
function(head, req){
provides(''html", function(){
bindings["page.title"] = keyword;
send(t.render(ddoc.templates.site.header, bindings));
send(t.render(ddoc.templates.timeline.header, bindings));
while(row = getRow()){
var doc = row.doc;
if( doc &&doc.type == 'tweets'){
$.extend(bindings, h.bind_tweet(doc));
send(t.render(ddoc.templates.timeline.row, bindings));
}
}
send(t.render(ddoc.templates.timeline.footer, bindings));
return t.render(ddoc.templates.site.footer, bindings);
});
}
Content-Length: chunked で送信開始
Content Negotiation
Viewの結果を1行ずつ取得して処理
CouchDBで実装したオンラインTV録画システム
http://bit.ly/bX5i7Q
ソース:
http://github.com/yssk22/recstore
世界中のCouchDBソフトウェアはCouchDB wiki を参照。
Server Side JavaScript Impl
CouchDBの場合は Spidermonkeyベースの実装
CGIのように動作する couchjsというProcess Poolが都度実行
○ CouchDBのメインプロセスとPipeで通信
それほど早くないので注意○ Server Side で実装してみて、遅ければブラウザに実行させ
るのもあり。 ドキュメントモデルと画面の差異が大きいほどJSで処理する量が多
くなるので、その場合は設計を疑うのも○。
実際どこまでできる? sandbox になっているので、ファイルを読んだり、外部リ
ソースにアクセスすることはできない。 JSの実装が気に入らなければ、Python, Ruby など他の実装
に切り替えることもできる。
まとめ
CouchDBとは、Webの一部となるドキュメント指向データベース
Web を支える HTTP で操作される
データベースとアプリケーションサーバーがJavaScriptで完結する
time to relax.