読者です 読者をやめる 読者になる 読者になる

中年engineerの独り言 - crumbjp

LinuxとApacheの憂鬱

MongoDBはDBではない。環境である!

勢いでtwiteしたついでに、軽く書いてみた。

MongoDBのfindAndModifyは物凄く便利で色々使い方があるのだが
$setOnInsertと組み合わせると、お手軽セマフォになるので
こんな感じで簡単にJOB管理に使える訳だ。

全ドキュメントを並列に処理する例

このスクリプトをmongo shell をいくつも立てて実行すれば、同一ドキュメントの重複処理を上手く回避して並列処理できるわけ。

foo
処理対照コレクション
foo_job
ジョブ管理用コレクション
// そのドキュメントが処理中か否か判定する
function isVacant(id){
  var prev = db.foo_job.findAndModify({
    query: {_id:id},
    update:{ $setOnInsert:{ tm:ISODate()}},
    upsert:true
  });
  if ( prev ) {
    return false;
  }
  return true;
}

// コレクション全部まわして、誰かが処理中のドキュメントはスキップ
var _c_foo = db.foo.find({});
  while ( _c_foo.hasNext() ) {
    var doc = _c_foo.next();
    if ( ! isVacant(doc._id) ) {
      continue;
    }
        :
    ココからdocを処理
              
  }
}

実際はもうちょっとエレガントに書いて再利用してる。
また、ドキュメントサイズが大きく、更に並列度を高くする場合は

var _c_foo = db.foo.find({},{_id:1});  

として、判定までは_idしか取って来なければ良い。
その後、自分が処理すると解った段階でfindOneで改めて取ってくると。。

色々やってみた所、MongoDBではIndexを使ったクエリーであっても性能に対する影響度は

データ量 << クエリーか良い数

のようで、

この辺りは扱うデータ次第。。大抵の場合は上の例で良い筈。


さてココから先はbashの話なので、やりたい人はこの辺りを見てって、、
https://github.com/crumbjp/analysis/blob/master/vectorize/bin/tf.sh


さてこれで、
プライマリノードのホストのCPUは使い切れるようになった。

(読み書きをPRIMARYに行うという意味)

セカンダリで処理する時の問題

セカンダリに接続する場合では、書き込みが出来ないので、その部分だけプライマリに逃がせば、後は一緒。

プライマリへのコネクションの張り方

var _pmongo = db.getMongo();
if ( ! rs.isMaster().ismaster ) {
  for ( var i in rs.status().members ) {
    var member = rs.status().members[i];
    if ( member.state === 1 ) {
      var conn = connect(member.name+'/'+db.getName());
      _pmongo = conn.getMongo();
      rs.slaveOk();
      break;
    }
  }
}

さして難しくないから、解説やーめたw

とにかくコレで_pmongoはプライマリへ向いた。

使うときはこんな感じか、、

  _pmongo.getDB('test').test.stats()

mongo shellを何処で動かすか?

当然、PRIMARYノードのホストからPRIMARYノードに繋げる場合が一番早い。

次に、SECONDARYノードのホストからSECONDARYノードに繋げ、上記の方法でPRIMARYへ書き込みする。

レプリカセット構成ホストの外から繋げてもなんの問題もなく、むしろ並列度を格段に上げる事ができるが、レイテンシーの影響が大きく、処理速度は数分の一になる。
プロセス数を大量に稼がないと元が取れない。