Mongodb支持使用javascript編寫mapreduce函數(shù)來做分布式數(shù)據(jù)處理胎署。然而,這種強(qiáng)大是有代價(jià)的:MapReduce非常慢,不應(yīng)該用在實(shí)時(shí)的數(shù)據(jù)分析中。
舉個(gè)MapReduce使用的例子拍屑,我們有一個(gè)orders表,包含cust_id,amount,status三個(gè)字段坑傅,現(xiàn)在我們有統(tǒng)計(jì)同一個(gè)cust_id的訂單的總額僵驰。
可以像下面這樣寫:
執(zhí)行順序顯而易見:
1、執(zhí)行query篩選出特定數(shù)據(jù)
2唁毒、執(zhí)行map返回鍵值對(duì)蒜茴,這里的值可以是一個(gè)list
3、執(zhí)行reduce對(duì)value求sum
4浆西、得到名為order_totals的結(jié)果
上面這個(gè)例子是掛在官網(wǎng)上的粉私。但是實(shí)際使用時(shí)按照這個(gè)寫就是無(wú)法通過....
詳細(xì)命令:
db.runCommand(
{
mapreduce : 字符串,集合名,
map : 函數(shù),見下文
reduce : 函數(shù)室谚,見下文
[, query : 文檔毡鉴,發(fā)往map函數(shù)前先給過渡文檔]
[, sort : 文檔崔泵,發(fā)往map函數(shù)前先給文檔排序]
[, limit : 整數(shù)秒赤,發(fā)往map函數(shù)的文檔數(shù)量上限]
[, out : 字符串猪瞬,統(tǒng)計(jì)結(jié)果保存的集合]
[, keeptemp: 布爾值,鏈接關(guān)閉時(shí)臨時(shí)結(jié)果集合是否保存]
[, finalize : 函數(shù)入篮,將reduce的結(jié)果送給這個(gè)函數(shù)陈瘦,做最后的處理]
[, scope : 文檔,js代碼中要用到的變量]
[, jsMode : 布爾值,是否減少執(zhí)行過程中BSON和JS的轉(zhuǎn)換潮售,默認(rèn)true] //注:false時(shí) BSON-->JS-->map-->BSON-->JS-->reduce-->BSON,可處理非常大的mapreduce,//true時(shí)BSON-->js-->map-->reduce-->BSON
[, verbose : 布爾值痊项,是否產(chǎn)生更加詳細(xì)的服務(wù)器日志,默認(rèn)true]
}
);
找了一個(gè)簡(jiǎn)單一點(diǎn)的例子酥诽,可以嘗試做一下鞍泉。
首先生成1000條測(cè)試數(shù)據(jù)。
for (var i=0; i<1000; i++) {
db.t.insert({
"name" : "user"+i,
"age":i ,
"created_at" : new Date()
});
}
創(chuàng)建map函數(shù):
var m=function(){
emit(this.age,this.name);
}
emit:返回一個(gè)鍵值對(duì)肮帐。emit的第一個(gè)參數(shù)是key咖驮,就是分組的依據(jù),這是自然是age了训枢,后一個(gè)是value托修,可以是要統(tǒng)計(jì)的數(shù)據(jù),下面會(huì)說明恒界,value可以是JSON對(duì)象睦刃。
這樣m就會(huì)把送過來的數(shù)據(jù)根據(jù)key分組了,可以想象成如下結(jié)構(gòu):
第一組
{key:0,values: ["name_6","name_12","name_18"]
第二組
{key:1,values: ["name_1","name_7","name_13","name_19"]
......
第二步就是簡(jiǎn)化了十酣,編寫reduce函數(shù):
var r=function(key,values){
var ret={age:key,names:values};
return ret;
}
reduce函數(shù)會(huì)處理每一個(gè)分組涩拙,參數(shù)也正好是我們想像分組里的key和values。
這里reduce函數(shù)只是簡(jiǎn)單的把key和values包裝了一下耸采,因?yàn)椴挥迷趺刺幚砭褪俏覀兿胍慕Y(jié)果了兴泥,然后返回一個(gè)對(duì)象。對(duì)象結(jié)構(gòu)正好和我們想象的相符洋幻!
最后郁轻,還可以編寫finalize函數(shù)對(duì)reduce的返回值做最后處理:
var f=function(key,rval){
if(key==0){
rval.msg="a new life,baby!";
}
return rval
}
這里的key還是上面的key,也就是還是age,rval是reduce的返回值文留,所以rval的一個(gè)實(shí)例如:{age:0,names:["name_6","name_12","name_18"]},
這里判斷 key 是不是 0 ,如果是而在 rval 對(duì)象上加 msg 屬性好唯,顯然也可以判斷 rval.age==0,因?yàn)?key 和 rval.age 是相等的嘛!燥翅!
db.runCommand({
mapreduce:"t",
map:m,
reduce:r,
finalize:f,
out:"t_age_names"
}
)
db.t_age_names.find()
結(jié)果導(dǎo)入到 t_age_names 集合中骑篙,查詢出來正是想要的結(jié)果,看一下文檔的結(jié)構(gòu)森书,不難發(fā)現(xiàn)靶端,_id 就是 key谎势,value 就是處理后的返回值。
map-reduce的性能優(yōu)化
這篇文章總結(jié)的很好了杨名,不再贅述:MongoDB MapReduce 性能提升20倍的優(yōu)化寶典
map-reduce運(yùn)行于sharding
比較一下map-reduce運(yùn)行于sharding和單個(gè)實(shí)例下的性能脏榆。
插入10萬(wàn)條數(shù)據(jù):
for (var i=0; i<100000; i++) {
db.t.insert({"name" : "user"+i,"age":i ,"created_at" : new Date()});
}
用map-reduce來查詢sharding下實(shí)例:
db.runCommand({ mapreduce:"t", map:m, reduce:r, finalize:f, sort:{"age":-1}, out:"t_age_names" })
{
"result" : "t_age_names",
"timeMillis" : 5082,
"counts" : {
"input" : 101000,
"emit" : 101000,
"reduce" : 1000,
"output" : 100000
},
"ok" : 1
}
運(yùn)行時(shí)間是5秒左右。
在單機(jī)單實(shí)例下運(yùn)行:
{
"result" : "t_age_names",
"timeMillis" : 4820,
"counts" : {
"input" : 101000,
"emit" : 101000,
"reduce" : 1000,
"output" : 100000
},
"ok" : 1
}
單機(jī)單實(shí)例下只需要4.8秒台谍。比單機(jī)sharding要快须喂。(因?yàn)閟harding是有開銷的)。
參考資料:
http://docs.mongodb.org/v2.6/core/map-reduce/
http://www.cnblogs.com/loogn/archive/2012/02/09/2344054.html