第2回 MongoDB JP 勉強会 in Tokyoに参加してきました

おまけに久しぶりに発表までしてきました。

資料→

ざっくり内容を書いておくと

  • mongodbは固定フォーマットではないデータをとりあえず投入して構造化できるので、解析初期に非常に便利
  • mongoimportは標準入力でいけるので、hadoop/hiveのstreamingと相性いい
  • mongoimportは標準入力でいけるので次のようにフィルタをかませられる
$ cat access.log | perl ./filter.pl | mongoimport -d mydb -c mycollection --fields ip,timestamp,method,host,uri,useragent
  • mongodbのmapreduceはkey/valueを縦横無尽に検索したり集計したりできるので、解析初期に便利
  • mongodbのmapreduceはhadoopのそれと比べると、sort&shuffleが隠されていて無い、(おそらく)それを補うようにfinalizeが付いてる
    • ただfinalizeは最終的なmapreduce成果物を全然入れ替えるほどの力がある
var out = 'hoge',
map = function(){
  emit(ua, 1);
},
reduce = function(k, vs){
  return Array.sum(vs);
},
finalize = function(k, out){
  var career = k.match(/(KDDI)|(DoCoMo)|(SoftBank)/);
  return {cnt: out, sumtime: new Date(), career: career[1]};
};
db.col.mapReduce(map, reduce, {fianlize: finalize, out: out});
db.hoge.find();
// もし、finalizeがなければしたみたいな{_id: UA, value: 回数}みたいなのが返ってくる
{_id: "ua1", value: 10}
{_id: "ua2", value: 20}
{_id: "ua3", value: 30}
{_id: "ua4", value: 40}

// でも、finalizeがあるので、したみたいな全然違うオブジェクトを作ることができる
{_id: "ua1", value: {cnt: 10, sumtime: ISODate(XXX), career: "KDDI"}}
{_id: "ua2", value: {cnt: 20, sumtime: ISODate(XXX), career: "DoCoMo"}}
{_id: "ua3", value: {cnt: 30, sumtime: ISODate(XXX), career: "KDDI"}}
{_id: "ua4", value: {cnt: 40, sumtime: ISODate(XXX), career: "SoftBank"}}
  • ただ、世の中にはmapreduceの実例がほとんど無い(googlabilityが低い)
  • mapReduceで重要なパラメタはフィルタリングの意味のquery(64bitでも、すぐにout of memory的なこというから)と、out.merge(出力先をmergeすることはaccess.log解析ではJKといっても過言ではない)
  • mongodbは特性上、フォーマットはでたらめでいいけどtimestampだけは最低合わせておく(ISODateでもNumberでも)
  • PV/UU/Session集計のサンプル書いておいた
    • PV
// 1時間ごとのPV
var col = db.logs,
  out = db.logs.hour,
  begin = new Date(),
  map = function(){
    var dt = new Date(this.timestamp),
        os = OSDetector(this.ua), browser = BrowserDetector(this.ua);
    emit({host: this.host, timestamp: new Date(dt.getFullYear(), dt.getMonth(), dt.getDay(), dt.getHours(), 0, 0)}, {cnt: 1, os: os, browser: browser);
  },
  reduce = function(k, vs){
    var out = {cnt: 0, os: {}, browser: {}};
    vs.forEach(function(v){
      out.cnt += v;
      if(out.os.hasOwnProperty(v.os)){ out.os[v.os] += 1; }else{ out.os[v.os] = 1; }
      if(out.browser.hasOwnProperty(v.browser)){ out.browser[v.browser] += 1; }else{ out.browser[v.browser] = 1; }
    });
    return out;
  },
  finalize = function(k, out){
    return out;
  };
col.mapReduce(map, reduce, {finalize: finalize, out: {merge: out}});
print('create col '+out+' begin '+begin+', end '+new Date());
    • UU
var
  o = 'logs.hour.uu.tmp',
  col = db.logs,
  map = function(){
    var dt = new Date(this.timestamp);
    emit({timestamp: new Date(dt.getFullYear(),dt.getMonth(),dt.getDay(),dt.getHours(), 0, 0),
          user: this.session, host: this.host}, 1);
  },
  reduce = function(k, vs){
    var sum = 0;
    vs.forEach(function(v){ sum += v; });
    return sum;
  };
col.mapReduce(map, reduce, {out:out});
var
  o = 'logs.hour.uu',
  col = db.logs.hour.uu.tmp,
  map = function(){
    emit({timestamp: this._id.timestamp, host: this._id.host}, {pv: this.value, uu: 1);
  },
  reduce = function(k, vs){
    var pv = 0, uu = 0;
    vs.forEach(function(v){
      pv += v.pv; uu += v.uu;
    });
    return {pv: pv, uu: uu};
  };
    • セッション集計 (counting session)
var
  o = 'logs.hour.session,
  map = function(){
    var dt = new Date(this.timestamp);
    if(!this.ref.indexOf(this.host)){
      // こういうところでちょっとした工夫が出来るところがmongodb mapReduceの面白いところ
      emit({timestamp: new Date(dt.getFullYear(), dt.getMonth(), dt.getDay(), dt.getHours(), 0, 0),
            user: this.session,
            host: this.host}, 1);
    }
  },
  reduce = function(k, vs){
    var sum = 0;
    vs.forEach(function(v){
      sum += v;
    });
    return sum;
  };
  • ごめんなさい。スライド後で見返したら、chrome以外うまく見られないかも >

mongodb benchmark (import/ find/ mapreduce) N=5

  • VM mini
process No Sharding x No Striping No Sharding x Striping Sharding x No Striping Sharding x Striping
mongoimport 9m9.201s 8m40.438s 10m30.101s 11m30.042s
find 38.37796 42.29296 13.61808 14.99836
mapreduce 226.64 229.46 107.39 106.85
  • VM large16
process No Sharding x No Striping No Sharding x Striping Sharding x No Striping Sharding x Striping
mongoimport 4m19.671s 1m44.757s 4m23.050s 3m13.803s
find 1.04364 1.0296 1.12412 1.13192
mapreduce 52.53 52.10 26.08 26.08