目次
MongoDB
MongoDBは、NoSQLの分散データベース
リレーショナルDBとの差異
MySQL | MongoDB | |
---|---|---|
種類 | リレーショナルDB | ドキュメント指向DB |
データ要素の集合 | テーブル | コレクション |
データ構造 | タプル(=行) | ドキュメント |
属性 | カラム | フィールド |
CAP定理 | 一貫性・可用性を保証 | 可用性・分断耐性を保証 |
トランザクション | あり | なし |
MongoDBのドキュメントはBSON(Binary JSON)形式で保存される。JSONで定義されているデータ(オブジェクト・配列・文字列・数値・真偽値・null)と日付(ISODate)、バイナリを保存できる。
トランザクションは存在しないがドキュメント単位でアトミックな操作が保証されている。ロックもドキュメント単位で行われる(mongoDB 3.0以降のwiredTigerエンジン使用時)
インストール
MacOSX
$ sudo port install mongodb
startup itemに追加
$ sudo port load mongodb
起動
自身の権限で起動してみる
$ mongod --dbpath=/Users/nullpon/data
dbpathはデータ保存場所、自身の権限で起動するので自分のホームディレクトリ以下のなど書き込み可能な適当な場所を指定する。dbpathのデフォルトは /data/db 。保存場所が存在しない場合や、読み書き権限が無い場合は起動に失敗するので予め作成しておく。デフォルトでPort 27017を使用する。
Linuxでは、 xfs でフォーマットされたディスク上のディレクトリを dbpath とする事。特に3系(WiredTiger)では必須、ext4ではパフォーマンスと安定性がかなり損なわれるため production環境では特に注意してください。ext4でサービスインしてひどい目にあっている話もちらほら聞きます。
インタラクティブシェル
mongoコマンドでインタラクティブシェルを起動する。
$ mongo
testという名前のDBに接続する。指定したdbが存在しなくても良い
$ mongo test
hostとDBを指定して起動
$ mongo localhost/test
host、port、DBを指定して起動
$ mongo localhost:27017/test
host、port、DBのデフォルトは localhost 27017 test である
mongoシェルではJavaScriptの文法でDBを操作する
> db.hoges.save({ fuga: 1, piyo: 2 }) > db.hoges.save({ fuga: 1, piyo: 3 }) > db.hoges.find() { "_id" : ObjectId("4ef94f405a360248562915f4"), "fuga" : 1, "piyo" : 2 } { "_id" : ObjectId("4ef94f6b5a360248562915f5"), "fuga" : 1, "piyo" : 3 }
※ save時にコレクションが存在しなくても良い
mongoシェルプロンプトを起動せずに実行
$ mongo dbname --quiet --eval="db.collectionName.count();" $ mongo localhost:27017/test < hogehoge.js
evalの場合、 show collections 等を実行できない
DB管理
DB削除
use DatabaseName; db.dropDatabase();
クエリ
mongoインタラクティブシェルで使えるクエリ
※ 以下Collectionには任意のコレクション名が入る
検索
db.Collection.find();
完全一致検索
db.Collection.find({name: "hogefuga"});
正規表現(前方一致)検索
db.Collection.find({name: /^hoge/});
ソート(1: asc, -1: desc)
db.Collection.find().sort({ name: 1 })
変数に結果を格納して利用できる
var a = db.CollectionA.findOne({_id: "hoge"}); db.CollectionB.find({ a_code: a.code });
ObjectId
ObjectIDは先頭4バイトがUNIX Timeなので、ドキュメントが時間フィールドを持っていなくても index を使った時間指定の範囲検索が可能
var start = Math.floor(new Date(2015, 0, 1, 0, 0, 0).getTime() / 1000); var end = Math.floor(new Date(2015, 0, 1, 23, 59, 59).getTime() / 1000); // mongo shellの場合 var start = ObjectId(start.toString(16) + '0000000000000000'); // Id, not ID var end = ObjectId(end.toString(16) + '0000000000000000'); db.HogeHoge.find({_id: {$gte: start, $lte: end}}); // node-mongodb-nativeの場合 var start = ObjectID.createFromTime(start); // ID, not Id var end = ObjectID.createFromTime(end); collection.find({_id: {$gte: start, $lte: end}}).each(function(err, item) { // ... });
インデックス
- B-Tree Index
- 注意点はRDBとほぼ同じ
- _idは常にIndexを持つ
作成
db.collection.createIndex({fieldName: 1});
TTL Index
TTL Indexを指定すると、指定した時間が過ぎるとドキュメントが削除される。Webアプリケーションのセッションデータの格納、一定期間だけ残したいログの保存などに向いている
制限
- インデックスを指定するフィールドはdate型の値でなくてはならない
- 複合インデックスはサポートしない
- Capped Collectionには指定できない
- あとから追加することも可能、ただしそのフィールドに既に通常のインデックスがある場合は作れない。インデックスを削除すればOK
例)3ヶ月ログを残す
db.OperationLog.createIndex({ "createdDate": 1 }, { expireAfterSeconds: 60 * 60 * 24 * 90 }); // ログ追加 db.OperationLog.insert({createdDate: new Date(), data: logData});
例)3600秒間更新がなければ削除する
db.UserSession.createIndex( { "lastModifiedDate": 1 }, { expireAfterSeconds: 3600 } ) // セッションを延長するため、TTL Indexフィールドを更新 db.UserSession.update({_id: sessionId}, { $set: { lastModifiedDate: new Date() }});
例)指定した時刻に削除する
db.UserSession.createIndex({"expiredDate": 1}, {expireAfterSeconds: 0}); // 有効期限の切れる時刻を設定してinsertする(2016/08/20 09:00で期限切れの場合) db.OperationLog.insert({expiredDate: new Date(2016, 7, 20, 9, 0, 0), data: logData});
管理画面
ブラウザでアクセスできるコンソールページ、mongodのport+1000のポートを使う。デフォルトは28017となる。
<html>–rest</html>オプションを付けて起動すると、RESTでDBを操作可能になる
Sharding
FAQ
can't do non-multi update with query that doesn't have a valid shard key
updateしようとすると、 can't do non-multi update with query that doesn't have a valid shard key
と出る
条件
- nodeのnativeドライバ
- コレクションがシャーディングされている
解決法
- updateのmultiフラグをtrueにする。
valid shard key must be in update object for collection
updateクエリで、 valid shard key must be in update object for collection:
というエラーが出る
条件
- nodeのnativeドライバ
- コレクションがシャーディングされている
解決法
- update object に _id を加える
- または、upsertで実行する
コネクション数上限を増やしたい
オープン可能なファイルディスクリプタ数の80%がコネクション数の上限。ulimit -n
でオープン可能なファイル数を増やす。
※ コネクション数の上限は、mongodに接続して db.serverStatus()
で表示されるconnectionsのcurrentとavailableの合計値
can't upsert something without full valid shard key
upsertクエリで、 can't upsert something without full valid shard key:
というエラーが出る
シャーディング環境では $in による複数 id 指定などでの upsert を受け付けないので、あらかじめ指定する id のドキュメントを作っておき、update で実行する。
full shard key must be in update object for collection
シャーディング環境のupdateクエリで発生。更新されるオブジェクトに_idが入っていないと発生する。
db.Collection.update({_id: "hoge"}, {value: 100});
以下のようにすればエラーにならないが、ドキュメント全体更新は必要なデータを消してしまう危険を伴うので $setや$unsetでプロパティを指定して変更する事をお勧めする。
db.Collection.update({_id: "hoge"}, {_id: "hoge", value: 100});
cannot use the part (hoge of hoge.fuga) to traverse the element ({hoge: 1.0})
オブジェクトではない値に対して、オブジェクトとしてアクセスしようとした
mongos> db.Test.find({_id: "a"}) { "_id" : "a", "hoge" : 1 } mongos> db.Test.update({_id: "a"}, {$set: {"hoge.fuga": 1}}) WriteResult({ "nMatched" : 0, "nUpserted" : 0, "nModified" : 0, "writeError" : { "code" : 16837, "errmsg" : "cannot use the part (hoge of hoge.fuga) to traverse the element ({hoge: 1.0})" } })
hogeオブジェクトのfugaプロパティを更新する操作を行ったが、hogeはオブジェクトではないためエラー