ユーザ用ツール

サイト用ツール


mongodb

MongoDB

MongoDBは、NoSQLの分散データベース

リレーショナルDBとの差異

MySQLMongoDB
種類リレーショナル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となる。

http://127.0.0.1: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はオブジェクトではないためエラー

mongodb.txt · 最終更新: 2020/02/26 02:55 by nullpon