後悔しないもんごもんごの使い方 〜アプリ編〜

58
後悔しない もんごもんごの使い方 ~アプリ編~ 松下 雅和(@matsukaz)

Upload: masakazu-matsushita

Post on 14-May-2015

6.280 views

Category:

Technology


3 download

DESCRIPTION

BPStudy 71回で発表した、MongoDBのアプリ寄りの使い方の話。

TRANSCRIPT

Page 1: 後悔しないもんごもんごの使い方 〜アプリ編〜

後悔しないもんごもんごの使い方

~アプリ編~松下 雅和(@matsukaz)

Page 2: 後悔しないもんごもんごの使い方 〜アプリ編〜

自己紹介

•松下 雅和•サーバ寄りのエンジニア•Twitter: @matsukaz•あだな:まつかず•DevLOVE スタッフ

Page 3: 後悔しないもんごもんごの使い方 〜アプリ編〜

今日の話•開発しやすいってほんと?•おすすめライブラリ(Java/Node.jsの話)

•データ設計はどうやるの?•こういう使い方するといいかも

Page 4: 後悔しないもんごもんごの使い方 〜アプリ編〜

開発しやすいってほんと?

Page 5: 後悔しないもんごもんごの使い方 〜アプリ編〜

ほんとだよ!

Page 6: 後悔しないもんごもんごの使い方 〜アプリ編〜

公式ドキュメントにも

MongoDB is an open-source, document-oriented database designed for ease of development and scaling.

MongoDBは、開発しやすさとスケールしやすさを目指したオープンソースのドキュメント指向DBです。

と書いてある

Page 7: 後悔しないもんごもんごの使い方 〜アプリ編〜

開発しやすいポイント①

Page 8: 後悔しないもんごもんごの使い方 〜アプリ編〜

インストールが簡単!

Page 9: 後悔しないもんごもんごの使い方 〜アプリ編〜

環境ごとのファイルをDLして展開するだけ

もろもろのパッケージにも対応

Page 10: 後悔しないもんごもんごの使い方 〜アプリ編〜

あとは起動するだけ

$ bin/mongod --fork \ --dbpath ./data \ --logpath ./logs/mongod.log

※ 必要最小限なオプションのみ

Page 11: 後悔しないもんごもんごの使い方 〜アプリ編〜

なにこれ簡単すぎ…?

@mongodb

>>あなたの適正もんごは?

Page 12: 後悔しないもんごもんごの使い方 〜アプリ編〜

開発しやすいポイント②

Page 13: 後悔しないもんごもんごの使い方 〜アプリ編〜

スキーマレス

Page 14: 後悔しないもんごもんごの使い方 〜アプリ編〜

どころか、DBやコレクションの

作成も不要(勝手に作られる)

Page 15: 後悔しないもんごもんごの使い方 〜アプリ編〜

$ bin/mongo

> use db1; // 操作するとDBが作られるswitched to db db1

// collectionが存在しなかったら作られる> db.user.save( { name : "matsukaz" } );

Page 16: 後悔しないもんごもんごの使い方 〜アプリ編〜

つまり簡単に使う分にはDB側の準備が全くいらない

Page 17: 後悔しないもんごもんごの使い方 〜アプリ編〜

まぢかよもんご!!!!クールすぎるぜもんご!そこにしびr(ry

Page 18: 後悔しないもんごもんごの使い方 〜アプリ編〜

開発しやすいポイント③

Page 19: 後悔しないもんごもんごの使い方 〜アプリ編〜

ドキュメント指向

Page 20: 後悔しないもんごもんごの使い方 〜アプリ編〜

RDBでやってた正規化とかめんどくない?

Page 21: 後悔しないもんごもんごの使い方 〜アプリ編〜

階層構造で持てるなら非正規化のままでもいいんでない?

Page 22: 後悔しないもんごもんごの使い方 〜アプリ編〜

プログラム上のオブジェクト構造をそのまま永続化も可能だし

Page 23: 後悔しないもんごもんごの使い方 〜アプリ編〜

試しにNode.jsで実装してみる

Page 24: 後悔しないもんごもんごの使い方 〜アプリ編〜

$ npm install mongodb # ドライバーのインストール

$ vi index.js

$ node index.js # 実行{ name: 'matsukaz', _id: 51f7efbed66a41de0f000001 }

var MongoClient = require("mongodb").MongoClient;var url = "mongodb://127.0.0.1:27017/db1";var data = {name : "matsukaz", age : 34}; // 作成したデータ

MongoClient.connect(url, function(err, db) { // 接続 var userColl = db.collection("user"); userColl.save(data, function(err) { // データを保存 userColl.findOne({name: "matsukaz"}, function(err, user) { // 名前で検索 console.log(user); process.exit(0); }); });});

Page 25: 後悔しないもんごもんごの使い方 〜アプリ編〜
Page 26: 後悔しないもんごもんごの使い方 〜アプリ編〜

こんだけ簡単だと使わない手はないよね!

Page 27: 後悔しないもんごもんごの使い方 〜アプリ編〜

おすすめライブラリ

Page 28: 後悔しないもんごもんごの使い方 〜アプリ編〜

Java•mongo-java-driver• https://github.com/mongodb/mongo-java-driver•MongoDBの公式ドライバー•ローレベルAPIなので操作はかなり面倒

Page 29: 後悔しないもんごもんごの使い方 〜アプリ編〜

Java•mongo-java-driver•コネクションプールのチューニングは必須

項目 意味 値connectionsPerHost コネクション数 100threadsAllowedToBlockForConnectionMultiplier

1コネクション辺りの接続待ち数 4

•上記の例だと、プールの上限は 100 + (100 * 4) = 500

Page 30: 後悔しないもんごもんごの使い方 〜アプリ編〜

Java•Spring Data - MongoDB• http://www.springsource.org/spring-data/mongodb•ドキュメントとPOJO間マッパー•mongo-java-driverを内部で利用

public class User { private String id; private String name; private int age; public User(String name, int age) { this.name = name; this.age = age; } // 各プロパティのgetter}

MongoTemplate mongoTemplate = new MongoTemplate(new Mongo(url), "dbname");// ドキュメント作成mongoTemplate.insert(new User("matsukaz", 34));// ドキュメント取得User matsukaz = mongoTemplate.findOne( new Query(Criteria.where("name").is("matsukaz")}, USer.class);

Page 31: 後悔しないもんごもんごの使い方 〜アプリ編〜

Javaでも言うほど相性は悪くない

Page 32: 後悔しないもんごもんごの使い方 〜アプリ編〜

Node.js•node-mongodb-native• https://github.com/mongodb/node-mongodb-native•MongoDBの公式ドライバー•スキーマ定義が不要ならこれで

require("mongodb").MongoClient.connect(url, function(err, db) { var userCollection = db.collection("user"); // ドキュメント作成 userCollection.save({name:"matsukaz", age:34}, function(err){ // ドキュメント取得 userCollection.findOne({name: "matsukaz"}, function(err, docs){ console.log(docs); }); });});

Page 33: 後悔しないもんごもんごの使い方 〜アプリ編〜

Node.js•Mongoose• http://mongoosejs.com•ドキュメントとオブジェクト間マッパー•node-mongodb-nativeを内部で利用

var db = require("mongoose").connect(url);// スキーマ定義が必要var User = db.model("user", new Schema({ name : { type : String, unique : true}, age : Number}));

var matsukaz = new User({name:"matsukaz", age:34});matsukaz.save(function(err) { // ドキュメント作成&取得 User.findOne({name:"matsukaz"}, function(err, user){ console.log(user); });});

Page 34: 後悔しないもんごもんごの使い方 〜アプリ編〜

Node.jsと本当に相性がいい!

Page 35: 後悔しないもんごもんごの使い方 〜アプリ編〜

MongoDBを使うと「開発がしやすくなる」のは伝わりました?

Page 36: 後悔しないもんごもんごの使い方 〜アプリ編〜

んでは次、

Page 37: 後悔しないもんごもんごの使い方 〜アプリ編〜

データ設計はどうやるの?

Page 38: 後悔しないもんごもんごの使い方 〜アプリ編〜

開発は簡単だけど、何も考えずに

データ構造を決めると運用で死にます・・・

Page 39: 後悔しないもんごもんごの使い方 〜アプリ編〜

データ構造を決める上で大事なポイントを整理

Page 40: 後悔しないもんごもんごの使い方 〜アプリ編〜

BSONの特徴に合わせる•BSONは複雑なデータ構造を扱える •RDBとは違い、積極的に階層化/非正規化

{ "facebookId" : xxx, "status" : { "lv" : 10, "coin" : 9999, ... }, "layerInfo" : "1|1|0|1|2|1|1|3|1|1|4|0...", "structure" : { "1|1" : { "id" : xxxx, "depth" : 3, "width" : 3, ... }, "4|8" : { "id" : xxxx, "depth" : 2, "width" : 2, ... } }, "neighbor" : [ { "id" : xxx, ... }, { "id" : xxx, ... } ]}

Page 41: 後悔しないもんごもんごの使い方 〜アプリ編〜

BSONの特徴に合わせる•スキーマレス = データ構造の変更が容易•開発を進めながら最適な構造にしていく•後方互換性も意識する

{ "dataId" : xxx, "update" : { "user" : xxx, "time" : xxx, "from" : "API" }}

{ "dataId" : xxx, "updateUser" : xxx }

{ "dataId" : xxx, "update" : { "user" : xxx }}

互換性を保ちやすい構造で

項目の追加は互換性が保ちやすい

Page 42: 後悔しないもんごもんごの使い方 〜アプリ編〜

BSONの特徴に合わせる•リレーションは、パフォーマンスも考えた上で、利用の有無とやり方を検討

•独自IDを利用

•ObjectIdを利用

•DBRefを利用

{ "userId" : 123, "manager" : 456 }

{ "userId" : 123, "manager" : ObjectId("4ba550e2b...") }

{ "userId" : 123, "manager" : DBRef("user", ObjectId("4ba550e2b...")) }

Page 43: 後悔しないもんごもんごの使い方 〜アプリ編〜

データ型の差異に注意•クライアントによっては、データ型のマッピングが異なるので注意が必要

•コンソール(mongo)•import/exportツール•言語別のドライバー/ライブラリ

Page 44: 後悔しないもんごもんごの使い方 〜アプリ編〜

苦手な部分は事前に考慮•トランザクションはない前提で•1ドキュメントにおけるデータ量/フィールド数が多すぎないように

•場合によってはデータを圧縮"layerInfo" : { "1|1" : 0, "1|2" : 1, ...}

"layerInfo" : "1|1|0|1|2|1|1|3|1|1|4|0..."

Page 45: 後悔しないもんごもんごの使い方 〜アプリ編〜

苦手な部分は事前に考慮•DBレベルのロックがかかる(v2.4時点)ので、アクセス頻度は極力減らす

•場合によっては、同じシステム内のコレクションでも、複数のDBに分割して保存する

Page 46: 後悔しないもんごもんごの使い方 〜アプリ編〜

シャードキーは慎重に•カーディナリティが低い値は使わない•利用頻度の高いデータがメモリ上に乗り、低いデータはメモリ上に乗らないように

•参照や更新が多いデータはバランスよく各Shardに分散

Page 47: 後悔しないもんごもんごの使い方 〜アプリ編〜

シャードキーは慎重に•極力Targetedオペレーションにする•Shard Keyでデータを操作•Shard Key以外の操作はIndexを利用

Operation Typedb.foo.find( { ShardKey : 1 } ) Targeteddb.foo.find( { ShardKey : 1, NonShardKey : 1 } ) Targeteddb.foo.find( { NonShardKey : 1 } ) Globaldb.foo.insert( <object> ) Targeteddb.foo.update( { ShardKey : 1 }, <object> )db.foo.remove( { ShardKey : 1 } )

Targeted

db.foo.update( { NonShardKey : 1 }, <object> )db.foo.remove( { NonShardKey : 1 } )

Global

Page 48: 後悔しないもんごもんごの使い方 〜アプリ編〜

気をつける点はそれなりに多いけど、

Page 49: 後悔しないもんごもんごの使い方 〜アプリ編〜

もんごの特性を理解していれば、

当たり前に考慮できるようになる・・はず

Page 50: 後悔しないもんごもんごの使い方 〜アプリ編〜

まずは使ってみることが大事!!

Page 51: 後悔しないもんごもんごの使い方 〜アプリ編〜

最後に、こういう使い方するといいかもという話

Page 52: 後悔しないもんごもんごの使い方 〜アプリ編〜

プロトタイプ開発•複雑なデータ構造を簡単に扱えるので、開発スピード重視で作れる

•スキーマレスなので、実装しながらデータ構造を変更できる

•ちょっとしたゲームのプロトタイプ開発(+3回の仕様変更)が2週間で出来たよ

Page 53: 後悔しないもんごもんごの使い方 〜アプリ編〜

開発中•開発中は以下のような方法を取ることが多い•各自ローカルでMongoDBを起動して開発•共用のMongoDBサーバを用意して用途別にDBを割り当てる

•各自の開発用•DEV環境用•Jenkinsによる単体テスト用

Page 54: 後悔しないもんごもんごの使い方 〜アプリ編〜

負荷テスト•コマンドラインツール(mongo)などから簡単に大量データを投入

•chunk移動しまくるので落ち着くまで注意> // 1000万件のテストデータを作成> function pad(str, length) { str = String(str); while (str.length < length) str = "0" + str; return str; }> for (var i = 1; i <= 10000000; i++) { db.user.save({name: ("hoge" + pad(i, 10)) }); }

Page 55: 後悔しないもんごもんごの使い方 〜アプリ編〜

障害テスト•障害を想定した動作検証は必ずやる。絶対。•mongodが落ちた場合•PRIMARYへ昇格するか•復帰後にデータが正しく同期されるか•mongocが落ちた場合に影響がないか•mongosが落ちた場合のシステムの挙動•バックアップから戻せるか

Page 56: 後悔しないもんごもんごの使い方 〜アプリ編〜

まとめ•開発での利用はほんとに簡単!•とりあえず作ってみよう!という場面で使えるのはもちろん

•使いどころを間違えなければサービスでもちゃんと使える

•もんごもんご!

Page 57: 後悔しないもんごもんごの使い方 〜アプリ編〜

宣伝!

•WEB+DB PRESS Vol.75 に 「MongoDB実践入門」を書きました!

Page 58: 後悔しないもんごもんごの使い方 〜アプリ編〜

ご清聴ありがとうございました!