在MongoDB的MapReduce上踩過的坑

來源:互聯網
上載者:User

標籤:style   blog   http   color   io   os   使用   ar   strong   

  太久沒動這裡,目前人生處於一個新的開始。這次部落格的內容很久前就想更新上來,但是一直沒找到合適的時間點(哈哈,其實就是懶),主要內容集中在使用Mongodb時的一些隱形MapReduce問題:

  1、Reduce時的計數問題

  2、Reduce時的提取資料問題

  另外,補充一個小tips:mongoDB中建立的索引,優先使用固定的,而不要使用範圍。

 

一、MapReduce時的計數問題

   這個問題主要出現在使用“+1”的思路去計算累計次數時。如果在Map後的某一類中,記錄量過大,就會導致計數失敗。

  具體示範如下:

  未經處理資料(有400條一樣的存在資料庫results表中):{ "grade" : 1, "name" : "lekko", "score" : 95 }   

  進行MapReduce:

 1 db.runCommand({ mapreduce: "results",  2  map : function Map() { 3     emit( 4         {grade:this.grade}, 5         {recnum:1,score:this.score} 6     ); 7 }, 8  reduce : function Reduce(key, values) { 9     var reduced = {recnum:0,score:0};10     values.forEach(function(val){11             reduced.score += val.score;12             ++reduced.recnum;13         });14     return reduced;15 },16 finalize : function Finalize(key, reduced) { 17         return reduced;18 },19 out : { inline : 1 }20 });

  滿懷希望地以為value.recnum會輸出400,結果卻是101!而value.scorce卻是輸出的正確的:38000(95*400)。本人在這疑惑了好久,並且通過更改reduce函數: function Reduce(key, values) { return {test:values}; } ,探索資料是這樣的:

  在原本Reduce函數中的forEach只遍曆了第一層的資料,即101個,所以++操作也只做了101次!

  經過思考,導致問題的原因關鍵就在於MapReduce中emit後的Bosn的資料格式,一個大於100的Array,會被拆分儲存,變成了非線性鏈表結構,

  那麼,分數相加卻能正確,可以大膽地推測:“reduced.score += val.score;” 語句可以智能地找到所有子結點的score並相加!

  最後,這裡給出計數的替代方案,修改Reduce的++,改用+=操作:

1 function Reduce(key, values) {    ;2     var reduced = {recnum:0,score:0};3     values.forEach(function(val){4             reduced.score += val.score;5             reduced.recnum += val.recnum;6         });7     return reduced;8 }


二、在Reduce中把資料提取出來組成Array

  

  這個問題產生的原因與上面的相似,也是由於emit後的資料在reduce時是非線性(有層次關係),所以提取資料欄位時也會產生問題,為了測試,往上面所說的表中再插入3條資料:

   { "grade" : 1, "name" : "monkey", "score" : 95 }, { "grade" : 2, "name" : "sudan", "score" : 95 }, { "grade" : 2, "name" : "xiaoyan", "score" : 95 } 

  編寫提取出各個grade的所有人名(不重複)列表:

 1 db.runCommand({ mapreduce: "results",  2  map : function Map() { 3     emit( 4         {grade:this.grade}, 5         {name:this.name} 6     );  7 }, 8  reduce : function Reduce(key, values) { 9     var reduced = {names:[]};10     values.forEach(function(val) {11         var isExist = false;12         for(var i = 0; i<reduced.names.length; i++) {13             var cur = reduced.names[i];14             if(cur==val.name){15                 isExist = true;16                 break;17             }18         }19         if(!isExist)20             reduced.names.push(val.name);21     });22     return reduced;23 },24  finalize : function Finalize(key, reduced) {25     return reduced;26 },27  out : { inline : 1 }28  });

  返回結果為:

1  { "_id" : {"grade" : 1},2    "value" :{ "names" : [null,"lekko"]}3  },4  { "_id" : {"grade" : 2},5    "value" :{ "names" : ["xiaoyan","sudan"]}6  }

  新插入的grade=2的兩條資料正常了,但grade=1的monkey卻不見了!採用問題一的思維方式,肯定也是在Reduce時遍曆到一個數組對象,其name值為空白,也給添加進來了,monkey對象根本就沒有訪問到。

  解決這一問題的方法是,拋棄MapReduce,改用Group:

 1 db.results.group({ 2  key : {"grade":true},  3  initial : {names:[]},  4  reduce : function Reduce(val, out) { 5     var isExist = false; 6     for(var i = 0; i<out.names.length; i++) { 7         var cur = out.names[i]; 8         if(cur==val.name){ 9             isExist = true;10             break;11         }12     }13     if(!isExist)14         out.names.push(val.name);    15 }, 16  finalize : function Finalize(out) {17     return out;18 }});

  這樣,便可正常取到grade=1時的name非重複集合!雖說MapReduce比Group要強大,速度也要快很多,但像這種要從大量項(超過100條)中提取資料,就有很大風險了。所以,使用MapReduce時,盡量只用到累加、累減、累乘等基本操作,不要去用++、push、delete等可能會產生風險的操作!

 

三、補充幾個小Tips

  1、使用Group或MapReduce時,如果一個分類只有一個元素,那麼Reduce函數將不會執行,但Finalize函數還是會執行的。這時你要在Finalize函數中考慮一個元素與多個元素返回結果的一致性(比如,你把問題二中插入一個grade=3的資料看看,執行返回的grade=3時還有names集合嗎?)。

  2、尋找範圍時的索引效率,如果查詢的是一個值的範圍,它索引的優先順序是很低的。比如一個表test,有海量元素,欄位有‘committime‘、‘author‘,建立了兩個索引:author_1、committime:-1,author:1,下面的測試證明了效率:

    db.test.find({‘committime‘:{‘$gt‘:910713600000,‘$lte‘:1410192000000},‘author‘:‘lekko‘}).hint({committime:-1,author:1}).explain()   "millis" : 49163
    db.test.find({‘committime‘:{‘$gt‘:910713600000,‘$lte‘:1410192000000},‘author‘:‘lekko‘}).explain()  author_1                 "millis" : 2641

  轉載請註明原址:http://www.cnblogs.com/lekko/p/3963418.html

在MongoDB的MapReduce上踩過的坑

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.