後悔しない
もんごもんごの使い方
∼アプリ編∼
松下 雅和(@matsukaz)
自己紹介
•松下 雅和
•サーバ寄りのエンジニア
•Twitter: @matsukaz
•あだな:まつかず
•DevLOVE スタッフ
今日の話
•開発しやすいってほんと?
•おすすめライブラリ
(Java/Node.jsの話)
•データ設計はどうやるの?
•こういう使い方するといいかも
開発しやすいって
ほんと?
ほんとだよ!
公式ドキュメントにも
MongoDB is an open-source, document-
oriented database designed for ease of
development and scaling.
MongoDBは、開発しやすさとスケールしやすさを目
指したオープンソースのドキュメント指向DBです。
と書いてある
開発しやすい
ポイント①
インストールが簡単!
環境ごとのファイルを
DLして展開するだけ
もろもろの
パッケージにも対応
あとは起動するだけ
$ bin/mongod --fork 
--dbpath ./data 
--logpath ./logs/mongod.log
※ 必要最小限なオプションのみ
なにこれ
簡単すぎ…?
@mongodb
>>あなたの適正もんごは?
開発しやすい
ポイント②
スキーマレス
どころか、
DBやコレクションの
作成も不要
(勝手に作られる)
$ bin/mongo
> use db1; // 操作するとDBが作られる
switched to db db1
// collectionが存在しなかったら作られる
> db.user.save( { name : "matsukaz" } );
つまり
簡単に使う分には
DB側の準備が
全くいらない
まぢかよもんご!!!!
クールすぎるぜもんご!
そこにしびr(ry
開発しやすい
ポイント③
ドキュメント指向
RDBでやってた
正規化とか
めんどくない?
階層構造で持てるなら
非正規化のままでも
いいんでない?
プログラム上の
オブジェクト構造を
そのまま永続化も
可能だし
試しに
Node.jsで実装してみる
$ 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);
});
});
});
こんだけ簡単だと
使わない手はないよね!
おすすめライブラリ
Java
•mongo-java-driver
• https://github.com/mongodb/mongo-java-driver
•MongoDBの公式ドライバー
•ローレベルAPIなので操作はかなり面倒
Java
•mongo-java-driver
•コネクションプールのチューニングは必須
項目 意味 値
connectionsPerHost コネクション数 100
threadsAllowedToBlockForC
onnectionMultiplier
1コネクション辺りの接続
待ち数
4
•上記の例だと、プールの上限は
100 + (100 * 4) = 500
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);
Javaでも
言うほど相性は
悪くない
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);
});
});
});
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);
});
});
Node.jsと
本当に相性がいい!
MongoDBを使うと
「開発がしやすくなる」
のは伝わりました?
んでは次、
データ設計は
どうやるの?
開発は簡単だけど、
何も考えずに
データ構造を決めると
運用で死にます・・・
データ構造を決める上で
大事なポイントを整理
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, ... } ]
}
BSONの特徴に合わせる
•スキーマレス = データ構造の変更が容易
•開発を進めながら最適な構造にしていく
•後方互換性も意識する
{
"dataId" : xxx,
"update" : {
"user" : xxx,
"time" : xxx,
"from" : "API"
}
}
{ "dataId" : xxx,
"updateUser" : xxx }
{ "dataId" : xxx,
"update" : {
"user" : xxx
}
}
互換性を保ち
やすい構造で
項目の追加は
互換性が保ちやすい
BSONの特徴に合わせる
•リレーションは、パフォーマンスも考えた上
で、利用の有無とやり方を検討
•独自IDを利用
•ObjectIdを利用
•DBRefを利用
{ "userId" : 123,
"manager" : 456 }
{ "userId" : 123,
"manager" : ObjectId("4ba550e2b...") }
{ "userId" : 123,
"manager" : DBRef("user", ObjectId("4ba550e2b...")) }
データ型の差異に注意
•クライアントによっては、データ型のマッピン
グが異なるので注意が必要
•コンソール(mongo)
•import/exportツール
•言語別のドライバー/ライブラリ
苦手な部分は事前に考慮
•トランザクションはない前提で
•1ドキュメントにおけるデータ量/フィールド
数が多すぎないように
•場合によってはデータを圧縮
"layerInfo" : {
"1¦1" : 0,
"1¦2" : 1, ...
}
"layerInfo" : "1¦1¦0¦1¦2¦1¦1¦3¦1¦1¦4¦0..."
苦手な部分は事前に考慮
•DBレベルのロックがかかる(v2.4時点)の
で、アクセス頻度は極力減らす
•場合によっては、同じシステム内のコレク
ションでも、複数のDBに分割して保存する
シャードキーは慎重に
•カーディナリティが低い値は使わない
•利用頻度の高いデータがメモリ上に乗り、低い
データはメモリ上に乗らないように
•参照や更新が多いデータはバランスよく各
Shardに分散
シャードキーは慎重に
•極力Targetedオペレーションにする
•Shard Keyでデータを操作
•Shard Key以外の操作はIndexを利用
Operation Type
db.foo.find( { ShardKey : 1 } ) Targeted
db.foo.find( { ShardKey : 1, NonShardKey : 1 } ) Targeted
db.foo.find( { NonShardKey : 1 } ) Global
db.foo.insert( <object> ) Targeted
db.foo.update( { ShardKey : 1 }, <object> )
db.foo.remove( { ShardKey : 1 } )
Targeted
db.foo.update( { NonShardKey : 1 }, <object> )
db.foo.remove( { NonShardKey : 1 } )
Global
気をつける点は
それなりに多いけど、
もんごの特性を
理解していれば、
当たり前に考慮できる
ようになる・・はず
まずは
使ってみることが
大事!!
最後に、
こういう使い方すると
いいかもという話
プロトタイプ開発
•複雑なデータ構造を簡単に扱えるので、開発ス
ピード重視で作れる
•スキーマレスなので、実装しながらデータ構造
を変更できる
•ちょっとしたゲームのプロトタイプ開発
(+3回の仕様変更)が2週間で出来たよ
開発中
•開発中は以下のような方法を取ることが多い
•各自ローカルでMongoDBを起動して開発
•共用のMongoDBサーバを用意して用途別に
DBを割り当てる
•各自の開発用
•DEV環境用
•Jenkinsによる単体テスト用
負荷テスト
•コマンドラインツール(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)) });
}
障害テスト
•障害を想定した動作検証は必ずやる。絶対。
•mongodが落ちた場合
•PRIMARYへ昇格するか
•復帰後にデータが正しく同期されるか
•mongocが落ちた場合に影響がないか
•mongosが落ちた場合のシステムの挙動
•バックアップから戻せるか
まとめ
•開発での利用はほんとに簡単!
•とりあえず作ってみよう!という場面
で使えるのはもちろん
•使いどころを間違えなければサービス
でもちゃんと使える
•もんごもんご!
宣伝!
•WEB+DB PRESS Vol.75 に
「MongoDB実践入門」を書
きました!
ご清聴
ありがとう
ございました!

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