MongoDB中MapReduce介紹與使用


一、簡介

在用MongoDB查詢返回的數據量很大的情況下,做一些比較復雜的統計和聚合操作做花費的時間很長的時候,可以用MongoDB中的MapReduce進行實現

MapReduce是個非常靈活和強大的數據聚合工具。它的好處是可以把一個聚合任務分解為多個小的任務,分配到多服務器上並行處理。MongoDB也提供了MapReduce,當然查詢語肯定是JavaScript。

MongoDB中的MapReduce主要有以下幾階段:

  • Map:把一個操作Map到集合中的每一個文檔

  • Shuffle: 根據Key分組對文檔,並且為每個不同的Key生成一系列(>=1個)的值表(List of values)。

  • Reduce: 處理值表中的元素,直到值表中只有一個元素。然后將值表返回到Shuffle過程,循環處理,直到每個Key只對應一個值表,並且此值表中只有一個元素,這就是MR的結果。

  • Finalize:此步驟不是必須的。在得到MR最終結果后,再進行一些數據“修剪”性質的處理。

MongoDB中使用emit函數向MapReduce提供Key/Value對。

Reduce函數接受兩個參數:Key,emits. Key即為emit函數中的Key。 emits是一個數組,它的元素就是emit函數提供的Value。

Reduce函數的返回結果必須要能被Map或者Reduce重復使用,所以返回結果必須與emits中元素結構一致。

Map或者Reduce函數中的this關鍵字,代表當前被Mapping文檔。

二、介紹

db.runCommand({  
    mapreduce:<collection>,  
    map:<mapfunction>,
    reduce:<reducefunction>,
    [,query:<query filter object>]
    [,sort:<sorts the input objects using this key.Useful for optimization,like sorting by the emit key for fewer reduces>]
    [,limit:<number of objects to return from collection>]
    [,out:<see output options below>]
    [,keeptemp:<true|false>]
    [,finalize:<finalizefunction>]
    [,scope:<object where fields go into javascript global scope>]
    [, jsMode : boolean,default true]
    [,verbose:true]
});

參數說明:

  • Mapreduce:要操作的目標集合

  • Map:映射函數(生成鍵值對序列,作為reduce函數參數)

  • Reduce:統計函數

  • Query:目標記錄過濾

  • Sort:目標記錄排序

  • Limit:限制目標記錄數量

  • Out:統計結果存放集合(不指定使用臨時集合,在客戶端斷開后自動刪除)

  • Keeptemp:是否保留臨時集合

  • Finalize:最終處理函數(對reduce返回結果進行最終整理后存入結果集合)

  • Scope:向map、reduce、finalize導入外部變量

  • jsMode說明:為false時 BSON-->JS-->map-->BSON-->JS-->reduce-->BSON,可處理非常大的mapreduce,為true時 BSON-->js-->map-->reduce-->BSON

  • Verbose:顯示詳細的時間統計信息

行查詢的步驟

  • MapReduce對指定的集合Collection進行查詢

  • 對A的結果集進行mapper方法采集

  • 對B的結果執行finalize方法處理

  • 最終結果集輸出到臨時Collection中

  • 斷開連接,臨時Collection刪除或保留

需要注意的

以下是來自文檔的圖,可以清楚的說明 Map-Reduce 的執行過程。

 

In this map-reduce operation, MongoDB applies the map phase to each input document (i.e. the documents in the collection that match the query condition). The map function emits key-value pairs. For those keys that have multiple values, MongoDB applies the reduce phase, which collects and condenses the aggregated data. MongoDB then stores the results in a collection. Optionally, the output of the reduce function may pass through a finalize function to further condense or process the results of the aggregation.

All map-reduce functions in MongoDB are JavaScript and run within the mongod process. Map-reduce operations take the documents of a single collection as the input and can perform any arbitrary sorting and limiting before beginning the map stage. mapReduce can return the results of a map-reduce operation as a document, or may write the results to collections. The input and the output collections may be sharded.

NOTE

For most aggregation operations, the Aggregation Pipeline provides better performance and more coherent interface. However, map-reduce operations provide some flexibility that is not presently available in the aggregation pipeline.

Map-Reduce 的執行過程是先 map 然后 reduce 么?仔細再看一遍上文的圖,不是每次 map 都有 reduce 的!如果 map 的結果不是數組,mongodb 就不會執行 reduce。很合理的處理邏輯。

對於 map 到的數據,如果在 reduce 時希望做統一的處理,一定會發現數據結果是不完整的。

三、查詢分析

測試數據:

> db.test.find()
{ "_id" : ObjectId("5a1d45ab893253f4d2e4bf91"), "name" : "yy1", "age" : "22" }
{ "_id" : ObjectId("5a1d45b1893253f4d2e4bf92"), "name" : "yy2", "age" : "23" }
{ "_id" : ObjectId("5a1d45c5893253f4d2e4bf93"), "name" : "yy3", "age" : "24" }
{ "_id" : ObjectId("5a1d45d4893253f4d2e4bf94"), "name" : "yy5", "age" : "25" }
{ "_id" : ObjectId("5a1d45f7893253f4d2e4bf95"), "name" : "yy6", "age" : "26" }
{ "_id" : ObjectId("5a1d45ff893253f4d2e4bf96"), "name" : "yy4", "age" : "25" }

1、查詢年齡大於23歲的

map:

var m  = function(){if(this.age > 23) emit(this.age,{name:this.name})};

reduce:

var r = function(key,values){return JSON.stringify(values);}

或者:

var r = function(key,values){ var ret={names:values};return ret;}

生成結果集:

var res = db.runCommand({mapreduce:"test",map:m,reduce:r,out:"emp_res"})

查詢:

> db.emp_res.find()
{ "_id" : "24", "value" : { "name" : "yy3" } }
{ "_id" : "25", "value" : "[{\"name\":\"yy5\"},{\"name\":\"yy4\"}]" }
{ "_id" : "26", "value" : { "name" : "yy6" } }

或者:

> db.emp_res.find()
{ "_id" : "24", "value" : { "name" : "yy3" } }
{ "_id" : "25", "value" : { "names" : [ { "name" : "yy5" }, { "name" : "yy4" } ] } }
{ "_id" : "26", "value" : { "name" : "yy6" } }

最后,還可以編寫finalize函數對reduce的返回值做最后處理:

var f=function(key,rval){ if(key==24){ rval.msg="do somethings";} return rval }

生成結果集:

> var f=function(key,rval){ if(key==24){ rval.msg="do somethings";} return rval }
> db.emp_res.find()
{ "_id" : "24", "value" : { "name" : "yy3", "msg" : "do somethings" } }
{ "_id" : "25", "value" : { "names" : [ { "name" : "yy5" }, { "name" : "yy4" } ] } }
{ "_id" : "26", "value" : { "name" : "yy6" } }
> 

2、過濾出來age=25的

方法1:

> var m  = function(){ emit(this.age,{name:this.name})};
> var r = function(key,values){ var ret={names:values};return ret;}
> var res = db.runCommand({mapreduce:"test",map:m,reduce:r,finalize:f,query:{age:"25"},out:"emp_res"})
> db.emp_res.find()
{ "_id" : "25", "value" : { "names" : [ { "name" : "yy5" }, { "name" : "yy4" } ] } }

方法2:

> var m  = function(){ emit(this.age,{p:[this.name]})};
> var r = function(key, values) {
    var ret = {p:[]};
    for(var i = 0; i < values.length; i++){
        ret.p.push(values[i].p[0]);
    }
    return ret;
};
> var res = db.runCommand({mapreduce:"test",map:m,reduce:r,finalize:f,query:{age:"25"},out:"emp_res"})

方法3:

> var m = function() {
    emit(this.age, {name:[this.name]});
};
> var r = func	tion(key, values) {
    var ret = {locs:[]}
    for(var i = 0; i < values.length; i++){
        ret.locs.push(values[i].locs[0]);
    }
    return ret;    
};
> var res = db.runCommand({mapreduce:"test",map:map,reduce:reduce,finalize:f,query:{age:"25"},out:"emp_res"})

這個過程中遇到很多坑,需要多練習,多debug

參考

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM