なんだそれ
MongoDBについて
・MongoDBはRDBMSではなく、いわゆるNoSQLと呼ばれるデータベースに分類されるものである。
ビッグデータとか扱うやつですね。
・「ドキュメント」と呼ばれる構造的データをJSONライクな形式で表現し、そのドキュメントの集合を「コレクション」として管理する
要するにユニークハッシュ値とjsonの組み合わせの連想配列。
・RDBMSのように高度な結合操作を効率的に行うことはできないが、データの追加・更新・削除・クエリは高速に行うことができる。
すごい。
・Mongoという名前は、英語で「ばかでかい」を意味する "humongous" に由来する。
笑った。
ビッグデータについて
市販されているデータベース管理ツールや従来のデータ処理アプリケーションで処理することが困難なほど巨大で複雑なデータ集合の集積物を表す用語である。
要するに超沢山データがある。
早速インストール
brew install mongodb
To have launchd start mongodb at login:
ln -sfv /usr/local/opt/mongodb/*.plist ~/Library/LaunchAgents
Then to load mongodb now:
launchctl load ~/Library/LaunchAgents/homebrew.mxcl.mongodb.plist
本体のインストール。
スタート時の設定もしようね、との事。
ln -sfv /usr/local/opt/mongodb/*.plist ~/Library/LaunchAgents
launchctl load ~/Library/LaunchAgents/homebrew.mxcl.mongodb.plist
言われた通りに、おまじないの実行。
mongo -version
MongoDB shell version: 3.2.0
インストール完了。やったぜ。
適当にデータを流してみる
調べてみると、どうやらmongoはjavaScriptでグリグリ出来るらしい。
適当な所にjsファイル作成、まずは意味ない適当なデータを突っ込んでみる。
cd
mkdir mongo
vi create.js
こうして。
for (var i = 0; i < 100; ++i) {
db.test.save({
hoge : "hoge" + i
});
}
適当にinsert分に当たるものを書いて
mongo --quiet localhost/test create.js
コレでmongoDBにズドン。 100件程度なので一瞬。
mongoDB上で見てみる
mongo
mongoにアクセス。
db.test.find()
{ "_id" : ObjectId("56a3111fcaf1a738f31cfce7"), "hoge" : "hoge0" }
{ "_id" : ObjectId("56a3111fcaf1a738f31cfce8"), "hoge" : "hoge1" }
{ "_id" : ObjectId("56a3111fcaf1a738f31cfce9"), "hoge" : "hoge2" }
...
おぉ!!入っている!
でもこういうデータだと、弄りようが無いので適当に妄想して遊ぶ事に。
db.test.drop()
今回作ったのは、もう使わないので消しちゃいましょう。
適当なシュミレーションとデータ設計
シュミレーション(妄想)
とあるホゲホゲ企業の購入履歴データを、過去5年間分、集計&調査する事になった可哀想な僕。
なんでも、ユーザー数は 1,000人程 だけど、ログ件数は 10,000,000件 ぐらいあるらしい。
妄想の世界の住人は、色々おかしい。
妄想の元に書いた、ログデータ作成スクリプト
const LOGS = 10000000;
const MAX_USER_ID = 1000;
const UNIXTIME_YEAR = 31536000;
const MAX_PURCHASE_PRICE = 10000;
function getRandomNumber(max) {
return Math.floor(Math.random() * (max + 1));
}
var date = new Date();
function getRandomUnixTime(offset) {
return Math.floor(date.getTime() / 1000) - getRandomNumber(offset);
}
for (var i = 0; i < LOGS; ++i) {
db.purchaseLog.save({
user_id : getRandomNumber(MAX_USER_ID),
purchase_price : getRandomNumber(MAX_PURCHASE_PRICE),
purchase_at : getRandomUnixTime(UNIXTIME_YEAR * 5),
});
}
コードの説明は不要かと思います。
上に書いた妄想通りです。
mongo --quiet localhost/test create.js
先ほどは100件程度のデータだったので一瞬で突っ込めましたが
今回は 10,000,000件 なので、流石にRDBMSより早いとは言っても、それなりの時間がかかります。
早く終る事を祈りつつズドン。
弄る準備をする。
10,000,000 件のデータが無事、クラッシュする事無くinsertされました。
ちなみに、私の ショボイ Mac pro(4G)だと 1時間7分23秒 かかりました。
早速作ったデータを見ていきます。
db.test.find()
local 0.000GB
test 0.404GB
10,000,000件入れても、jsonのサイズ自体は小さいので400MB程度でした。
db.test.find()
{ "_id" : ObjectId("56a31a2d201cdb2f439ca4ae"), "user_id" : 655, "purchase_price" : 9357, "purchase_at" : 1438883196 }
{ "_id" : ObjectId("56a31a2e201cdb2f439ca4af"), "user_id" : 816, "purchase_price" : 6915, "purchase_at" : 1380445364 }
{ "_id" : ObjectId("56a31a2e201cdb2f439ca4b0"), "user_id" : 851, "purchase_price" : 4786, "purchase_at" : 1382838307 }
...
一応データも確認、想定通りです。
このまま弄るのも良いのですが、ちょっと色々と面倒なのでラッパーを書きます。
弄る為のjavaScriptを適当に用意。
var results = db.purchaseLog.find({user_id:100});
// suck
function formatDate(unixtime) {
var date = new Date(unixtime * 1000);
date.setTime(date.getTime() + (60 * 60 * 1000));
var zeroPadding = function(num) {
return ("0" + num).slice(-2);
}
var year = date.getFullYear();
var month = zeroPadding(date.getMonth() + 1);
var day = zeroPadding(date.getDate());
var hour = zeroPadding(date.getHours());
var min = zeroPadding(date.getMinutes());
var sec = zeroPadding(date.getSeconds());
return year + "-" + month + "-" + day + " " + hour + ":" + min + ":" + sec;
}
var counter = 0;
results.forEach(function(result) {
++counter;
print("id: " + result.user_id + "\t\t" + "price: " + result.purchase_price + "\t" + "date: " + formatDate(result.purchase_at));
});
print(counter);
大事な所は、一番上の行だけです。
db.purchaseLog.find(...) の所が、SQLのクエリ部分です。
サンプルで、単にuser_idが100番の人を抽出してみました。
後は表示に必要な処理を書いただけです。
注意点としては、データの取得条件は、特殊な場合を除いてfind()部分で取得するようにして下さい。
いくら早くてもforEach部分で処理してしまうと、パフォーマンスが落ちます。
後、javaScriptの日付操作って超面倒なんですね
実際に動かす
mongo --quiet localhost/test find.js
id: 100 price: 9617 date: 2012-08-20 16:42:24
id: 100 price: 5410 date: 2014-12-11 17:48:45
id: 100 price: 1506 date: 2011-05-30 11:18:03
...
3秒ぐらいで結果が出ました、早い、早すぎる。
RDBMS上で処理すると、1分以上はかかる処理ではないでしょうか。
色々条件を変えたりして弄ってみると分かりますが、 どれもメチャ早いです。
まとめ
予想以上に早い
10,000,000件のデータを弄っているとは思えない程早くて驚きました。
この例だと、javaScriptを流して10秒以上待たされる事は無かったです。
RDBMSはマスターデータおじさんで、noSQLはトランデータお兄さんな印象を受けました。
一般的にビックデータと言われる、膨大なデータを扱う事に興味を持った方は
一度弄ってみてはどうでしょうか。
世界が変わります。
ちなみに
db.purchaseLog.aggregate([
{ $group: { _id: "$user_id", total: { $sum: "$purchase_price" }}},
{ $sort: { total: 1}}
]).forEach(function(result) {
print("id: " + result._id + "\t\t" + "totalPrice: " + result.total);
});
...
id: 795 price: 51749038
過去5年通して、一番使ってた人。
複雑な条件になると、ちょっとネストが見づらい。 ymlで書きたい。
このコードはidでグループ化して合計金額を全員分集計し、ソートしていますが
実行速度は、14秒でした。
パネェ!!
おわり。