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

7,480 views
7,279 views

Published on

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

Published in: Technology

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

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

×