2012年6月6日水曜日

JSXを使ってみたメモ


話題のjsxを使ってみたので、その際のメモを残します。


■JSXの売り
・高速(関数のインライン展開、定数の畳み込みなど最適化)
・静的型付け言語でチームでの大規模開発に向いている
・javascriptの技術者は容易に習得可能
・Javaっぽく書ける

特徴はここにまとまっています
http://ginpen.com/2012/05/31/jsx/

JSXはなぜ「速い」のか
http://d.hatena.ne.jp/kazuhooku/20120602/1338641072

チュートリアル
http://jsx.github.com/tutorial.html

ブラウザからコンパイラの動作を試せます
http://jsx.github.com/try/


■使ってみた感想
・javaっぽく、かなり綺麗に書ける
・クラス分割してモジュール開発といった手法がかなりとりやすい
・外部ライブラリや既存の資産のうち、javascriptの特性(prototype継承など)に依存しているものは移植しにくい
・javascriptの特性を柔軟に利用したいならcoffee-scriptの方が向いている

外部ライブラリ(jQuery)を組み込んだ例
http://d.hatena.ne.jp/nishiohirokazu/20120531/1338462986


■コンパイラの動き(わかったことだけ)
・デバッグモードとリリースモードがある。
・jsxとjsで関数名が変わる(引数の型や数を表す識別子がつく)
・実行時エラーが飛ぶような型チェックは、デバッグモードでのみ行われる。
・リリースモードの場合、パフォーマンスのために型チェックは行われない。(公式ドキュメントに記述あり)
※ Run-time type checks can be omitted by compiling the source code with the --release option


2012年5月26日土曜日

hbaseでトランザクションを利用する方法


hbaseのトランザクションについて補足
デフォルトではトランザクションをサポートしていないけど、
プラグインを入れることでトランザクションを使えるようになるとのこと。(RDBMSのそれとは若干異なる。)

方法を2つ見つけたので共有。
  1. contribのプラグインhbase-trx/hbase-transactional-tableindexedをhbaseに追加して起動する。@github
    • 楽観的平行性制御というトランザクション形式
    • →begin()時にロックするのではなく、割り込みがあった場合は全更新をロールバックして例外を返す。
  2. サードパーティーのライブラリhbase rowlog libraryを「クライアントサイドに」入れる。
    • ロックしないのでデッドロックを考えずに分散スキーマに向く。
    • 方法(あってるかな)
      1. キューにトランザクションを全てためる。
      2. 先頭クエリでこけた場合は処理を全て行わない。
      3. 2つ目以降のクエリでこけた場合は、バックグラウンドの別スレッドがキュー上の残クエリを実行する。(非同期)
参考:
HBase0.89におけるトランザクションについて -Hadoopユーザー会 | Google グループ

2012年5月13日日曜日

MongoDBのObjectIDから作成日時を取り出す

■ 概要
ObjectIDの構成は下記になっており、先頭4byteからcreated timeを取得できる。

0123456 7891011
timemachine pidinc


mongos> obj = new ObjectId()
ObjectId("4faf45d47f8e775a2d1a6948")
mongos> createdDt = new Date(parseInt(obj.toString().substr(0, 8), 16) * 1000)
ISODate("2012-05-13T05:25:40Z")

2012年5月12日土曜日

MongoDBのマイグレーション(Migration)中のcountの挙動について検証したときのメモ

# ----------------------------------
概要
# ----------------------------------
MongoDBのマイグレーション中に起きるcount()の問題を検証した。
↓↓問題については@doryokujinさんのslide share資料
Mongo sharding
↓↓こちらの記事も検証、図がわかりやすかったです。
MongoDBのShardingを試してみた。その2 Migration中の挙動について


シャードキーで検索、全件検索、snapshot()での検索、index()での検索、いずれの場合でもcount()結果がずれる。


# ----------------------------------
# mongo dbのバージョンは2系
# ----------------------------------
mongod --version
db version v2.0.4, pdfile version 4.5
Fri May 11 21:59:13 git version: 329f3c47fe8136c03392c8f0e548506cb21f8ebf

# ----------------------------------
# 構成
# ----------------------------------
macbook-air-13inch: {
  mongos : [27017],
  config : [11100],
  sharding: {
    replica_set1 : {
      shards: [10011, 10012],
    },
    replica_set2 : {
      shards: [10021, 10022],
    },
  }
}
# arbiterは省略

# 1,000万件のデータ投入
use ryooo
ins_humongous = function(){
  n = Math.random() * 100000000
  db.test_humongous.insert({number: parseInt(n)})
}
for (i = 0; i <= 10000000; i++) {
  ins_humongous()
}


# 最初は798969件だった
mongos> use ryooo
switched to db ryooo
mongos> db.test_humongous.find({ number : {$gt: 100,  $lt: 8000000}}).count();
798969

# numberにindexはついていない
mongos> db.test_humongous.stats()
{
 ・
 ・
 "totalIndexSize" : 324603552,
 "indexSizes" : {
  "_id_" : 324603552
 },
}


# ----------------------------------
# migration開始
# ----------------------------------
mongos> use admin
switched to db admin
mongos> db.runCommand({shardcollection : "ryooo.test_humongous", key: {_id : 1}});
{ "collectionsharded" : "ryooo.test_humongous", "ok" : 1 }


# migration中
mongos> use ryooo
switched to db ryooo
mongos> db.test_humongous.getShardDistribution()

Shard Shard1 at Shard1/localhost:10011,localhost:10012
 data : 311.77Mb docs : 8172914 chunks : 13
 estimated data per chunk : 23.98Mb
 estimated docs per chunk : 628685

Shard Shard2 at Shard2/localhost:10021,localhost:10022
 data : 75Mb docs : 1966082 chunks : 3
 estimated data per chunk : 25Mb
 estimated docs per chunk : 655360

Totals
 data : 386.77Mb docs : 10138996 chunks : 16
 Shard Shard1 contains 80.6% data, 80.6% docs in cluster, avg obj size on shard : 40b
 Shard Shard2 contains 19.39% data, 19.39% docs in cluster, avg obj size on shard : 40b


# migration後
mongos> db.test_humongous.getShardDistribution()

Shard Shard1 at Shard1/localhost:10011,localhost:10012
 data : 181.47Mb docs : 4757215 chunks : 8
 estimated data per chunk : 22.68Mb
 estimated docs per chunk : 594651

Shard Shard2 at Shard2/localhost:10021,localhost:10022
 data : 200Mb docs : 5242887 chunks : 8
 estimated data per chunk : 25Mb
 estimated docs per chunk : 655360

Totals
 data : 381.47Mb docs : 10000102 chunks : 16
 Shard Shard1 contains 47.57% data, 47.57% docs in cluster, avg obj size on shard : 40b
 Shard Shard2 contains 52.42% data, 52.42% docs in cluster, avg obj size on shard : 40b


# migration中の件数(約20秒ごと)(コマンドは一部省略)
# indexを張っていないnumberで検索
mongos> use ryooo
switched to db ryooo
mongos> b.test_humongous.find({ number : {$gt: 100,  $lt: 8000000} } ).count();
798969 ←開始時
798969
820015 ←ずれはじめ
828251
851267
・
(すべて正常値以上の値)
・
890386
840958
798969 ←終了時(開始時と同じ)




# ----------------------------------
# 次は、indexを張ってやればどうか。
# 初期化
use ryooo
db.test_humongous.drop()

# indexをはる
db.test_humongous.ensureIndex({number:1})

# 1,000万件のデータ投入
use ryooo
ins_humongous = function(){
  n = Math.random() * 100000000
  db.test_humongous.insert({number: parseInt(n)})
}
for (i = 0; i <= 10000000; i++) {
  ins_humongous()
}

# numberにindexがついている状態
mongos> db.test_humongous.stats()
{
 ・
 ・
 "indexSizes" : {
  "_id_" : 324464560,
  "number_1" : 356841520
 },
}

# ----------------------------------
# migration開始
# ----------------------------------
# 最初は799226件だった(コマンドは一部省略)
mongos> use ryooo
switched to db ryooo
mongos> db.test_humongous.find({ number : {$gt: 100,  $lt: 8000000}}).count();
799226 ←開始時
803179
809402
814710
・
(すべて正常値以上の値)
・
813520
802336
799226 ←終了時(開始時と同じ)



# 全件検索(コマンドは一部省略)
mongos> db.test_humongous.count()
10000001 ←開始時
10000001
10481154
10470561
10438490
・
(すべて正常値以上の値)
・
10215171
10067667
10000001 ←終了時(開始時と同じ)



# snapshot()しても結果は変わらず(コマンドは省略)
mongos> db.test_humongous.find().snapshot().count()
10013371 ←途中から気づいてとり始めた
10009113
10005426
・
(すべて正常値以上の値)
・
10222059
10092911
10000001 ←終了時(全件検索数と一致)



# シャードキーでの検索も同様にずれる。
# 1回目のmigrationでの計測では、初回の1回だけ異常値で後は正常値であった。
# 2回目のmigrationでの計測では、すべてのcountがずれた。
#   これはシャードキーでの検索ではglobalでなく、
#   対象のchunkが存在するシャードにのみ検索が走るため、
#   対象のchunkがいつmoveするかで結果が異なる。

# 1回目のmigration(コマンドは一部省略)
mongos> db.test_humongous.find({ _id : {$lt: ObjectId('4fad11b830e2c797dc8ec095')}}).count();
2966108 ←開始時
1810734
1810734
・
(すべて正常値)
・
1810734
1810734 ←終了時

# 2回目のmigration
mongos> db.test_humongous.find({ _id : {$lt: ObjectId('4fadb6a76d5f062fe7f155eb')}}).count();
6022486 ←開始時
6310732
6966093
7621454
6719822
5753037
6210336
5592768
5655372
5233997
5000011 ←終了時

やはりProduction環境ではauto-shardingを停止した方が良いな。

Macにsleepy.mongooseをインストールしたときのメモ

# ----------------------------------
■概要
# ----------------------------------
mongodb のRestインターフェース sleepy.mongooseをインストールしたときのメモ
このAPIをたたいてグラフ化したら、可視化の開発スピードが早くなるねー!

# ----------------------------------
■install
# ----------------------------------
easy_install pymongo
git clone https://github.com/kchodorow/sleepy.mongoose.git

cd sleepy.mongoose/
vi sleepymongoose/handlers.py
python httpd.py
Traceback (most recent call last):
  File "httpd.py", line 17, in 
    from handlers import MongoHandler
  File "/Users/matsumura/dev/mongoose/sleepy.mongoose/sleepymongoose/handlers.py", line 16, in 
    from pymongo.son import SON
ImportError: No module named son

# エラーでた
#from pymongo.son import SON
from bson.son import SON

python httpd.py

=================================
|      MongoDB REST Server      |
=================================

listening for connections on http://localhost:27080

# 起動した

# とれたー
curl http://localhost:27080/ryooo/userActivity/_find
{"ok": 1, "results": [{"_id": {"$oid": "4fabb349dfac802c09d4d6ab"}, "type": "login", "name": "ruffy", "at": {"$date": 1336233600000}}, {"_id": {"$oid": "4fabb349dfac802c09d4d6ac"}, "type": "login", "name": "zoro", "at": {"$date": 1336237200000}}, {"_id": {"$oid": "4fabb349dfac802c09d4d6ad"}, "type": "login", "name": "usopp", "at": {"$date": 1336240800000}}, {"_id": {"$oid": "4fabb349dfac802c09d4d6ae"}, "type": "login", "name": "nami", "at": {"$date": 1336244400000}}, {"_id": {"$oid": "4fabb349dfac802c09d4d6af"}, "type": "login", "name": "ruffy", "at": {"$date": 1336161600000}}, {"_id": {"$oid": "4fabb349dfac802c09d4d6b0"}, "type": "login", "name": "zoro", "at": {"$date": 1336165200000}}, {"_id": {"$oid": "4fabb349dfac802c09d4d6b1"}, "type": "login", "name": "nami", "at": {"$date": 1336168800000}}, {"_id": {"$oid": "4fabb349dfac802c09d4d6b2"}, "type": "login", "name": "ruffy", "at": {"$date": 1336086000000}}, {"_id": {"$oid": "4fabb349dfac802c09d4d6b3"}, "type": "login", "name": "usopp", "at": {"$date": 1336089600000}}, {"_id": {"$oid": "4fabb349dfac802c09d4d6b4"}, "type": "login", "name": "nami", "at": {"$date": 1336093200000}}, {"_id": {"$oid": "4fabb349dfac802c09d4d6b5"}, "type": "login", "name": "ruffy", "at": {"$date": 1336010400000}}, {"_id": {"$oid": "4fabb349dfac802c09d4d6b6"}, "type": "login", "name": "zoro", "at": {"$date": 1336014000000}}, {"_id": {"$oid": "4fabb349dfac802c09d4d6b7"}, "type": "login", "name": "ruffy", "at": {"$date": 1335931200000}}, {"_id": {"$oid": "4fabb349dfac802c09d4d6b8"}, "type": "login", "name": "zoro", "at": {"$date": 1335934800000}}, {"_id": {"$oid": "4fabb349dfac802c09d4d6b9"}, "type": "login", "name": "usopp", "at": {"$date": 1335938400000}}], "id": 0}

2012年5月5日土曜日

MongoDBのMapReduceを使ってみたときのメモ

■MapReduce の決まり
・map()でemit()した値の形式とreduce()の戻り値の形式が一致していること reduce関数は一度のMapReduce内で何度も呼ばれ、全てのreduceの戻り値をreduceした結果がMapReduceの戻り値となる。
→最終的な結果 = reduce(key, [values_3, reduce(key, [values_2, reduce(key, [values_1])])])

また、map()内で1件しかemit()されなかったkeyと値は、reduce()で一度も処理されない!!

すべてreduceした後の値を処理したい場合は、finalize functionを利用できる。

mongoの公式マニュアルが詳しい。
MapReduce - Docs-Japanese - 10gen Confluence

(mongoは集計できるので)mongoでは使いどころは限られると思うが、簡単にjavascriptでMapReduceできるなんてMongoDBやっぱいいね(・∀・)


■ MapReduceでやる意味(長所)
・集計対象のデータが巨大になっても負荷分散できる。
・oracleのマテビューのように結果を保存できる。(merge, replaceも可能)
・そもそもcassandraやhbaseでは集計関数やキー以外の条件検索ができないが、MapReduceなら集計できる。
そのため集計用のテーブルなどを別に作成せず、ユーザーのトランザクションデータを直接集計できる。


■ データの初期化
// データの初期化
db.userActivity.remove()

// 登録
db.userActivity.insert({name: 'ruffy', type: 'login', at: new Date('2012/05/06 1:00:00')});
db.userActivity.insert({name: 'zoro', type: 'login', at: new Date('2012/05/06 2:00:00')});
db.userActivity.insert({name: 'usopp', type: 'login', at: new Date('2012/05/06 3:00:00')});
db.userActivity.insert({name: 'nami', type: 'login', at: new Date('2012/05/06 4:00:00')});
db.userActivity.insert({name: 'ruffy', type: 'login', at: new Date('2012/05/05 5:00:00')});
db.userActivity.insert({name: 'zoro', type: 'login', at: new Date('2012/05/05 6:00:00')});
db.userActivity.insert({name: 'nami', type: 'login', at: new Date('2012/05/05 7:00:00')});
db.userActivity.insert({name: 'ruffy', type: 'login', at: new Date('2012/05/04 8:00:00')});
db.userActivity.insert({name: 'usopp', type: 'login', at: new Date('2012/05/04 9:00:00')});
db.userActivity.insert({name: 'nami', type: 'login', at: new Date('2012/05/04 10:00:00')});
db.userActivity.insert({name: 'ruffy', type: 'login', at: new Date('2012/05/03 11:00:00')});
db.userActivity.insert({name: 'zoro', type: 'login', at: new Date('2012/05/03 12:00:00')});
db.userActivity.insert({name: 'ruffy', type: 'login', at: new Date('2012/05/02 13:00:00')});
db.userActivity.insert({name: 'zoro', type: 'login', at: new Date('2012/05/02 14:00:00')});
db.userActivity.insert({name: 'usopp', type: 'login', at: new Date('2012/05/02 15:00:00')});
db.userActivity.insert({name: 'nami', type: 'login', at: new Date('2012/05/02 16:00:00')});
db.userActivity.insert({name: 'ruffy', type: 'login', at: new Date('2012/05/01 17:00:00')});
db.userActivity.insert({name: 'usopp', type: 'login', at: new Date('2012/05/01 18:00:00')});

db.userActivity.insert({name: 'ruffy', type: 'event_login', at: new Date('2012/05/06 19:00:00')});
db.userActivity.insert({name: 'nami', type: 'event_login', at: new Date('2012/05/06 20:00:00')});
db.userActivity.insert({name: 'zoro', type: 'event_login', at: new Date('2012/05/05 21:00:00')});
db.userActivity.insert({name: 'nami', type: 'event_login', at: new Date('2012/05/05 22:00:00')});
db.userActivity.insert({name: 'usopp', type: 'event_login', at: new Date('2012/05/04 23:00:00')});
db.userActivity.insert({name: 'nami', type: 'event_login', at: new Date('2012/05/04 1:00:00')});
db.userActivity.insert({name: 'ruffy', type: 'event_login', at: new Date('2012/05/03 2:00:00')});
db.userActivity.insert({name: 'zoro', type: 'event_login', at: new Date('2012/05/03 3:00:00')});
db.userActivity.insert({name: 'ruffy', type: 'event_login', at: new Date('2012/05/02 4:00:00')});
db.userActivity.insert({name: 'nami', type: 'event_login', at: new Date('2012/05/02 5:00:00')});
db.userActivity.insert({name: 'ruffy', type: 'event_login', at: new Date('2012/05/01 6:00:00')});


// 検索
db.userActivity.find({name: 'ruffy'}).sort({at:-1});
db.userActivity.find({name: 'zoro'}).sort({at:-1});


■ 誤った方法
MapReduceの決まりに沿っていないため、動きはするがデータ量が増えるとNG
決まり:map()でemit()した値の形式とreduce()の戻り値の形式が一致していること
// ------------------------------------------------
// ユーザー軸の集計
map = function () {
  // 'ruffy', {type: login, dt: 2012-05-05}
  emit(this.name, {'type': this.type, 'dt': this.at});
}
reduce = function(key, values) {
  var result = {login: [], event_login: []};
  values.forEach(function(value) {
    type = value.type
    dt = value.dt
    // javascriptの日付操作、なんとかならんかの。
    dt.setHours(0);dt.setMinutes(0);dt.setSeconds(0);dt.setMilliseconds(0);
    result[type].push(dt)
  });
  return result;
}
// 実行(inlineオプションは、結果を保存せず画面出力するオプション)
db.userActivity.mapReduce(map, reduce, {out: { inline : 1}});

// とはいえ、これはユーザーでorderすれば同じようなことができる
db.userActivity.find().sort({name: 1, at: -1, type:1})


// ------------------------------------------------
// 日付軸の集計
map = function () {
  dt = this.at;
  dt.setHours(0);dt.setMinutes(0);dt.setSeconds(0);dt.setMilliseconds(0);
  // 2012-05-05, {type: login, name: 'ruffy'}
  emit(dt, {'type': this.type, 'name': this.name});
}
reduce = function(key, values) {
  var result = {login: [], event_login: []};
  values.forEach(function(value) {
    type = value.type
    result[type].push(value.name)
  });
  return result;
}
// 実行(inlineオプションは、結果を保存せず画面出力するオプション)
db.userActivity.mapReduce(map, reduce, {out: { inline : 1}});

// sqlだとdate型のカラムがない状態で集計は面倒だけど楽にできちゃうヽ(・∀・)ノ

// 対象データをfilterして集計とかもできちゃう
db.userActivity.mapReduce(map, reduce, {query: {at: {$gte: new Date('2012/05/03')}}, out: {inline : 1}});


// ------------------------------------------------
// 活動軸の集計
map = function () {
  // login, {name: 'ruffy', dt: 2012-05-05}
  emit(this.type, {'name': this.name, 'dt': this.at});
}
reduce = function(key, values) {
  var result = {};
  values.forEach(function(value) {
    dt = value.dt
    dt.setHours(0);dt.setMinutes(0);dt.setSeconds(0);dt.setMilliseconds(0);
    if (typeof result[dt] == 'undefined') {
      result[dt] = 0
    }
    result[dt] += 1
  });
  return result;
}
// 実行(inlineオプションは、結果を保存せず画面出力するオプション)
db.userActivity.mapReduce(map, reduce, {out: { inline : 1}});

db.userActivity.mapReduce(map, reduce, {query: {type: 'login'}, out: { inline : 1}});


■ 正しい方法
// ------------------------------------------------
// ユーザー軸の集計
map = function () {
  // 'ruffy', {type: login, dt: 2012-05-05}
  dt = this.at
  dt.setHours(0);dt.setMinutes(0);dt.setSeconds(0);dt.setMilliseconds(0);
  value = {}
  value[this.type] = [dt]
  emit(this.name, value);
}
reduce = function(key, values) {
  var result = {};
  values.forEach(function(value) {
    for (type in value) {
      for (i in value[type]) {
        if (typeof result[type] == 'undefined') { result[type] = []; }
        dt = value[type][i]
        result[type].push(dt)
      }
    }
  });
  return result;
}
// 実行(inlineオプションは、結果を保存せず画面出力するオプション)
db.userActivity.mapReduce(map, reduce, {out: { inline : 1}});


// ------------------------------------------------
// 日付軸の集計
map = function () {
  dt = this.at
  dt.setHours(0);dt.setMinutes(0);dt.setSeconds(0);dt.setMilliseconds(0);
  value = {}
  value[this.type] = [this.name]
  emit(dt, value);
}
reduce = function(key, values) {
  var result = {};
  values.forEach(function(value) {
    for (type in value) {
      for (i in value[type]) {
        if (typeof result[type] == 'undefined') { result[type] = []; }
        dt = value[type][i]
        result[type].push(dt)
      }
    }
  });
  return result;
}
// 実行(inlineオプションは、結果を保存せず画面出力するオプション)
db.userActivity.mapReduce(map, reduce, {out: { inline : 1}});



// ------------------------------------------------
// 活動軸の集計
map = function () {
  dt = this.at
  dt.setHours(0);dt.setMinutes(0);dt.setSeconds(0);dt.setMilliseconds(0);
  value = {}
  value[dt] = 1
  emit(this.type, value);
}
reduce = function(key, values) {
  var result = {};
  values.forEach(function(value) {
    for (dt in value) {
      if (typeof result[dt] == 'undefined') { result[dt] = 0; }
      count = value[dt];
      result[dt] += parseInt(count)
    }
  });
  return result;
}
// 実行(inlineオプションは、結果を保存せず画面出力するオプション)
db.userActivity.mapReduce(map, reduce, {out: { inline : 1}});



■ 参考サイト MongoDBのMapReduceは、その他にも様々なオプションでいろいろできる!
MapReduce - Docs-Japanese - 10gen Confluence - MongoDB

Macにmongodbをインストールしたときのメモ

■ Homebrewで楽々インストール

brew install mongodb

mkdir -p ~/dev/mongo/data

# 起動
mongod --dbpath=~/dev/mongo/data

# クライアント起動
mongo


■ クライアントでいくつかのクエリを実行

db.UserReport.insert({
  name: 'ruffy',
  pt: 1000,
  crystal: {
    big: 0,
    small: 300
  },
});
db.UserReport.insert({
  name: 'zoro',
  pt: 2000,
  crystal: {
    big: 45,
    small: 600
  },
});
db.UserReport.insert({
  name: 'nami',
  pt: 3000,
  crystal: {
    big: 40,
    small: 400
  },
});
db.UserReport.insert({
  name: 'sanji',
  pt: 4000,
  crystal: {
    big: 20,
    small: 100
  },
});
db.UserReport.insert({
  name: 'usopp',
  pt: 5000,
  crystal: {
    big: 30,
    small: 0
  },
});


// 全件取得
db.UserReport.find()

// where pt = 1000
db.UserReport.find({pt: 1000})

// 多次元(組み込みobject)の値で検索
db.UserReport.find({"crystal.small": 100})
db.UserReport.find({"crystal.small": {$lt: 300}}).sort({pt: -1})

// 全件削除
db.UserReport.remove()
やっぱ運用の評判は悪くても、mongoがいいねヽ(・∀・)ノ

■クエリはこちらを参考に。
クエリー - Docs-Japanese - 10gen Confluence


■Mongoのベンチマークとってみた。

・gem
sudo gem install mongo bson bson_ext

・ソース
#!/usr/bin/env ruby
require 'rubygems'
require 'mongo'
require 'benchmark'

# ---------------------------------------
# Insert / Select 性能
# ---------------------------------------
count = 100000
p 'count:' + count.to_s
 
# ---------------------------------------
# mongodb
m = Mongo::Connection.new('localhost', 27017)
db = m.db('testdb')

row = {
  :user_id => 0,
  :name => 'name0',
  :created_at => Time.now.to_i
}
Benchmark.bm { |rep|
  rep.report("mongodb insert ") {
    for num in 1..count
      db = m.db('testdb')
      name = 'name' + num.to_s
      row[:user_id] = num
      row[:name] = name
      db['user'].insert(row)
    end
  }
}
Benchmark.bm { |rep|
  rep.report("mongodb select") {
    for num in 1..count
      id = rand(count).to_i + 1
      db['user'].find({:user_id => id})
    end
  }
}

・結果
mongod --version
db version v2.0.4, pdfile version 4.5

# MongoDB
"count:100000"
      user     system      total        real
mongodb insert  13.430000   1.030000  14.460000 ( 14.457051)
      user     system      total        real
mongodb select  4.680000   0.070000   4.750000 (  4.768458)

# 過去の結果
# MySQLはInnoDB
"count:100000"
      user     system      total        real
mysql(o/r) insert  74.320000   5.670000  79.990000 (110.039184)
      user     system      total        real
mysql(o/r) select 50.610000   2.650000  53.260000 ( 65.465168)
      user     system      total        real
mysql(sql) insert   4.430000   1.530000   5.960000 ( 29.590552)
      user     system      total        real
mysql(sql) select  5.470000   1.780000   7.250000 ( 13.550391)
      user     system      total        real
cassandra insert  47.180000   3.140000  50.320000 ( 61.347904)
      user     system      total        real
cassandra select  69.150000   3.470000  72.620000 (111.568860)
      user     system      total        real
memcache insert  14.930000   2.280000  17.210000 ( 17.935214)
      user     system      total        real
memcache select  16.710000   2.080000  18.790000 ( 19.264883)
      user     system      total        real
handlersocket insert   1.430000   1.510000   2.940000 ( 26.721995)
      user     system      total        real
handlersocket select   1.320000   1.520000   2.840000 (  7.867557)

# MySQLはSpider
"count:100000"
      user     system      total        real
mysql(o/r) insert 101.200000   7.280000 108.480000 (341.227877)
      user     system      total        real
mysql(o/r) select 71.910000   3.450000  75.360000 (294.436478)
      user     system      total        real
mysql(sql) insert   9.000000   2.600000  11.600000 (235.837247)
      user     system      total        real
mysql(sql) select  8.750000   2.700000  11.450000 (245.967628)

# 前提として、1台のmacで行っているため、スケールアウトのメリットを享受できていません。
# そのため、Cassandra やhbaseでは期待する効果が出ていません!!


■ その他のベンチマーク詳細
Macのsandboxを使ってspiderを動かしてみたメモ
MacのMySQLでHandlerSocketのベンチマークをとった
※すべてMac1台で行っているので、適切にスケールの効果が見られないものもあります。Spiderとかね。