ワダと申します。
今年こそ、桜が咲いているうちに宴会付きのお花見に行きたいです。
前回はSQLServerについての記事を書きました。
今回もDB関連になりますが、RDBMSから一旦離れて、RDBMSに対抗する概念である「NoSQL」に注目してみます。
今回は、NoSQLの中でも注目度の高い「mongoDB」を取り上げます。
NoSQLとは
「NoSQL(一般に “Not only SQL” と解釈される)とは、関係データベース管理システム (RDBMS) 以外のデータベース管理システムを指すおおまかな分類語である。」
mongoDBとは
「MongoDBはRDBMSではなく、いわゆるNoSQLと呼ばれるデータベースに分類されるものである。
RDBMSのようにレコードをテーブルに格納するのではなく、「ドキュメント」と呼ばれる構造的データをJSONライクな形式で表現し、
そのドキュメントの集合を「コレクション」として管理する(このデータの物理的な格納はBSONと呼ばれるJSONのバイナリ版といえる形式で行われる)。」
英語で「ばかでかい」を意味する “humongous” に由来する。
・大量で複雑な構造のデータを高速に処理するための設計が施されている。
・データ整合性よりも速度が求められる場合、スキーマを事前に定義できない場合に便利である。
mongoDBを取り上げることにした動機
私はRDBMSが今も昔もデータベース管理システムの主流であり続けると思っていました。
が、最近(でもないかも知れませんが・・)JSONを保存する需要などのためにNoSQLがかなり注目されているようです。
データベースをメインテーマとして追及し続けたい自分としては、NoSQLについても会話についていける程度には押さえておきたいと思ったのがきっかけです。
mongoDBのインストールやクエリ実行についての情報は、ネット上でそこそこ見つかりました。
しかし、ビッグデータの処理に適しているとされながら、時間とリソースがそれなりに必要となるためか、実際に試している例はあまり見つかりません。
ですので、私はこれをやってみようと考えました。
■さっそく、mongoDBのインストール
今回使用したPC
・OS Windows10
・CPU Intel core m 6y54
・搭載メモリ 8GB
mongoDBをインストールし、Pathの設定などを行います。
私はここ(MongoDBのインストール)のサイトを参照しました。
DBデータ保存ディレクトリは以下のようにしました。dataディレクトリにデータファイルが格納されます。
1
2
3
4
5
|
C:\mongodb
└\server
├\data
├\log
└ mongod.cfg
|
設定ファイル(mongod.cfg)
1
2
3
4
5
|
systemLog:
destination: file
path: C:\mongodb\server\log\mongod.log
storage:
dbPath: C:\mongodb\server\data
|
■起動
mongoDBを起動します。コマンドは「mongod」。
1
|
> mongod --dbpath "C:\mongodb\server\data"
|
コマンドを叩くとコンソールに色々表示されますが、
最終行に「 waiting for connections on port 27017」とでていれば正常に起動できています。
次にクライアントからアクセス。コマンドは「mongo」です。
■クライアントからデータベースへアクセス
アクセスできたら、手始めにドキュメントを1件作成します。
・ドキュメント ≒ RDBMSにおける行に相当
・コレクション ≒ RDBMSにおけるテーブルに相当。
・データベース ≒ 複数のコレクションを格納できる。複数作成可能。
以下のようにコマンドを実行します。
1
2
|
> db.test.insert({ "key1" : "value1", "key2" : "value2" })
WriteResult({ "nInserted" : 1 })
|
これで「testというコレクションに”key1″と”key2″というフィールド名で”value1″と”value2″という値のドキュメントを1件作成した」
という状態になりました。
ちゃんと作成できているか、ドキュメントを参照します。
findコマンドを使います。RDBMSでいう「SELECT * from テーブル名」です。
1
2
|
> db.test.find()
{ "_id" : ObjectId("5aba205cf5c882b184bfbf1d"), "key1" : "value1", "key2" : "value2" }
|
1件作成できていました。
insertもfindも、SQLに慣れている人が直感的に行えるコマンドになっているので助かります。
いよいよ次に、ビッグデータ投入を試してみます。
何をもってビッグデータとするか、にも諸説あるようなのですが、今回は「QAtest」という名前のコレクションに、
ドキュメント数50,000,000のコレクションを作成します。
先ほどはmongoコマンドを叩いて、mongoシェル上でinsertコマンド、findコマンドを実行しましたが、
クエリをjsファイルとしてあらかじめ用意してmongoクライアントに渡す方法もあります。
(Wikipediaによると、JavascriptはMongoDBにおいて「共通語」とされているようです。)
「JavaScriptはMongoDBにおけるLingua franca(共通語)であり、クエリや集約関数(MapReduce等)で使用したり、
データベースに直接送信して実行できる。」
50,000,000件のデータを作成するためのJavascriptファイルを用意しました。
「insertQA.js」
1
2
3
4
5
6
7
8
9
10
11
12
13
|
const QA = 50000000;
const CONTENT = 10000;
function getRandom(max) {
return Math.floor(Math.random() * (max + 1));
}
for (var i =0; i < QA; i++) {
db.QAtest.save({
QA_id :getRandom(QA),
content :getRandom(CONTENT)
});
}
|
かなりの時間を要することを覚悟して、insertを実行します。
1
|
> mongo --quiet localhost/QAtest insertQA.js
|
エラーが何も出ずにプロンプトが出れば完了です。
かかった時間は・・・約9時間でした。
確認するため、mongoシェルを起動し、以下のコマンドでコレクション「QAtest」のドキュメント数を確認します。
1
2
|
> db.getCollectionNames().forEach(function(n){print(n + "," + db[n].count())})
QAtest,50000000
|
50,000,000件、ちゃんと入りました。
データのサイズは 約1.7GBとなっています。
1
2
|
> show dbs
QAtest1.729GB
|
DBデータ保存ディレクトリの中身。
dataディレクトリにデータファイルなどが格納されています。
■条件を指定して抽出
QA_idが「1」のデータを抽出します。
1
2
3
|
> db.QAtest.find({QA_id:1})
{ "_id" : ObjectId("5aba5bc5de24118cab0327ed"), "QA_id" : 1, "content" : 8338 }
{ "_id" : ObjectId("5aba699fdde3650da6535554"), "QA_id" : 1, "content" : 2553 }
|
スマホのストップウォッチでざっくり測ったところ約37秒で結果が出ました。
もしかしたら、インデックスを作成すれば速くなるかも??
ということでQA_idにインデックスを作成します。
1
2
3
4
5
6
7
|
> db.QAtest.createIndex({QA_id:1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
|
インデックス作成後に再び検索すると、こんどは計測するまでもなく一瞬で結果が出ました。
■更新
「QA_id」の値が「12345678」のドキュメントの「content」の値を「aaa」に更新します。スキーマ定義がないので、数値から文字へ変更しても大丈夫です。
1
2
|
> db.QAtest.update({user_id:12345678},{$set:{content:'aaa'} })
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
|
かかった時間は約3秒でした。速いです。
mongoDBにはトランザクションがないのでコミット、ロールバックはありません。
■ソート
QAtestコレクションを、qa_idフィールドで昇順ソートします。
1
2
3
4
5
6
7
|
> db.QAtest.find().sort({qa_id:1})
Error: error: {
"ok" : 0,
"errmsg" : "Executor error during find command: OperationFailed: Sort operation used more than the maximum 33554432 bytes of RAM. Add an index, or specify a smaller limit.",
"code" : 96,
"codeName" : "OperationFailed"
}
|
・・・怒られてしまいました。ソート用のバッファサイズが足りないというようなエラーが出ています。
https://stackoverflow.com/questions/27023622/overflow-sort-stage-buffered-data-usage-exceeds-internal-limit
ソート用のバッファを増やします。管理者用のコマンドです。
1
2
3
4
|
> use admin
switched to db admin
> db.adminCommand({setParameter: 1, internalQueryExecMaxBlockingSortBytes:50151432})
{ "was" : 33554432, "ok" : 1 }
|
バッファを増やして再度実施したのですが、うまくいきませんでした。
こちらについては別途調べてみます。
■おわりに
mongoDBでは、スキーマの設計をせずにデータをどんどん蓄積できるところが魅力的です。
また、Javascript上でクエリとロジックを駆使すると、もっと色々と複雑な操作ができるようです。
mongoDBは、大量のログ保存と解析といった用途に特に適していると思いますが、
本格的に業務で利用するには こちらのようにElasticSearchなどOSSの検索エンジンと組み合わせる方法を検討すると良さそうです。
(現在私が所属する現場においても事例があります。)
今回は以上です。ありがとうございました。