在MongoDB的MapReduce上踩过的坑

时间:2022-01-13 23:35:27

  太久没动这里,目前人生处于一个新的开始。这次博客的内容很久前就想更新上来,但是一直没找到合适的时间点(哈哈,其实就是懒),主要内容集中在使用Mongodb时的一些隐蔽的MapReduce问题:

  1、Reduce时的计数问题

  2、Reduce时的提取数据问题

  另外,补充一个小tips:mongoDB中建立的索引,优先使用固定的,而不要使用范围。

一、MapReduce时的计数问题

  这个问题主要出现在使用“+1”的思路去计算累计次数时。如果在Map后的某一类中,记录量过大,就会导致计数失败。

  具体演示如下:

  原始数据(有400条一样的存在数据库results表中):{ "grade" : 1, "name" : "lekko", "score" : 95 }

  进行MapReduce:

 db.runCommand({ mapreduce: "results",
map : function Map() {
emit(
{grade:this.grade},
{recnum:1,score:this.score}
);
},
reduce : function Reduce(key, values) {
var reduced = {recnum:0,score:0};
values.forEach(function(val){
reduced.score += val.score;
++reduced.recnum;
});
return reduced;
},
finalize : function Finalize(key, reduced) {
return reduced;
},
out : { inline : 1 }
});

  满怀希望地以为value.recnum会输出400,结果却是101!而value.scorce却是输出的正确的:38000(95*400)。本人在这疑惑了好久,并且通过更改reduce函数: function Reduce(key, values) { return {test:values}; } ,发现数据是这样的:

在MongoDB的MapReduce上踩过的坑

  在原本Reduce函数中的forEach只遍历了第一层的数据,即101个,所以++操作也只做了101次!

  经过思考,导致问题的原因关键就在于MapReduce中emit后的Bosn的数据格式,一个大于100的Array,会被拆分存储,变成了非线性的链表结构,如图:

在MongoDB的MapReduce上踩过的坑

  那么,分数相加却能正确,可以大胆地推测:“reduced.score += val.score;” 语句可以智能地找到所有子结点的score并相加!

  最后,这里给出计数的替代方案,修改Reduce的++,改用+=操作:

 function Reduce(key, values) {    ;
var reduced = {recnum:0,score:0};
values.forEach(function(val){
reduced.score += val.score;
reduced.recnum += val.recnum;
});
return reduced;
}

二、在Reduce中把数据提取出来组成Array

  

  这个问题产生的原因与上面的相似,也是由于emit后的数据在reduce时是非线性的(有层次关系),所以提取数据字段时也会产生问题,为了测试,往上面所说的表中再插入3条数据:

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

  编写提取出各个grade的所有人名(不重复)列表:

 db.runCommand({ mapreduce: "results",
map : function Map() {
emit(
{grade:this.grade},
{name:this.name}
);
},
reduce : function Reduce(key, values) {
var reduced = {names:[]};
values.forEach(function(val) {
var isExist = false;
for(var i = 0; i<reduced.names.length; i++) {
var cur = reduced.names[i];
if(cur==val.name){
isExist = true;
break;
}
}
if(!isExist)
reduced.names.push(val.name);
});
return reduced;
},
finalize : function Finalize(key, reduced) {
return reduced;
},
out : { inline : 1 }
});

  返回结果为:

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

  新插入的grade=2的两条数据正常了,但grade=1的monkey却不见了!采用问题一的思维方式,肯定也是在Reduce时遍历到一个数组对象,其name值为空,也给添加进来了,monkey对象根本就没有访问到。

  解决这一问题的方法是,抛弃MapReduce,改用Group:

 db.results.group({
key : {"grade":true},
initial : {names:[]},
reduce : function Reduce(val, out) {
var isExist = false;
for(var i = 0; i<out.names.length; i++) {
var cur = out.names[i];
if(cur==val.name){
isExist = true;
break;
}
}
if(!isExist)
out.names.push(val.name);
},
finalize : function Finalize(out) {
return out;
}});

  这样,便可正常取到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上踩过的坑的更多相关文章

  1. mongodb3&period;6 (五)net 客户端访问mongodb设置超时时间踩过的&OpenCurlyDoubleQuote;坑”

    前言 在上一篇文章中,我们有提到net访问mongodb连接超时默认为30秒,这个时间在实际项目中肯定是太长的.而MongoClientSettings 也确是提供了超时属性,如下图: 可实际使用中, ...

  2. Redis上踩过的一些坑

    来自: http://blog.csdn.net//chenleixing/article/details/50530419 上上周和同事(龙哥)参加了360组织的互联网技术训练营第三期,美团网的DB ...

  3. MongoDB 的 MapReduce 大数据统计统计挖掘

    MongoDB虽然不像我们常用的mysql,sqlserver,oracle等关系型数据库有group by函数那样方便分组,但是MongoDB要实现分组也有3个办法: * Mongodb三种分组方式 ...

  4. MongoDb 用 mapreduce 统计留存率

    MongoDb 用 mapreduce 统计留存率(金庆的专栏)留存的定义采用的是新增账号第X日:某日新增的账号中,在新增日后第X日有登录行为记为留存 输出如下:(类同友盟的留存率显示)留存用户注册时 ...

  5. mongoDB实现MapReduce

    一.MongoDB Map Reduce Map-Reduce是一种计算模型,简单的说就是将大批量的工作(数据)分解(MAP)执行,然后再将结果合并成最终结果(REDUCE).MongoDB提供的Ma ...

  6. MongoDB:Map-Reduce

    Map-reduce是一个考虑大型数据得到实用聚集结果的数据处理程式(paradigm).针对map-reduce操作,MongoDB提供来mapreduce命令. 考虑以下的map-reduce操作 ...

  7. &lbrack;转帖&rsqb;美团在Redis上踩过的一些坑-1&period;客户端周期性出现connect timeout

    美团在Redis上踩过的一些坑-1.客户端周期性出现connect timeout 博客分类: redis 运维 jedisconnect timeoutnosqltcp  转载请注明出处哈:http ...

  8. 美团在Redis上踩过的一些坑-目录(本人非美团)(转)

    来自:http://carlosfu.iteye.com/blog/2254154 分为5个部分:    一.周期性出现connect timeout    二.redis bgrewriteaof问 ...

  9. 记录近期小改Apriori至MapReduce上的心得

    ·背景 前一阵,一直在研究一些ML的东东,后来工作关系暂停了一阵.现在继续把剩下一些热门的算法再吃吃透,"无聊+逗比"地把他们搞到MapReduce上.这次选择的入手对象为Apri ...

随机推荐

  1. JDBC学习总结&lpar;一&rpar;

    1.JDBC概述     JDBC是一种可以执行SQL语句并可返回结果的Java API,其全称是Java DataBase Connectivity,也是一套面向对象的应用程序接口API,它由一组用 ...

  2. php实现和c&num;一致的DES加密解密

    原文:php实现和c#一致的DES加密解密 php实现和c#一致的DES加密解密,可以从网上搜到一大堆,但是测试后发现都没法用.以下正确代码是我经过苦苦才找到的.希望大家在系统整合时能用的上. 注意: ...

  3. 【JAVA零基础入门系列】Day11 Java中的类和对象

    今天要说的是Java中两个非常重要的概念--类和对象. 什么是类,什么又是对象呢?类是对特定集合的概括描述,比如,人,这个类,外观特征上,有名字,有年龄,能说话,能吃饭等等,这是我们作为人类的相同特征 ...

  4. 一、源代码-面向CLR的编译器-托管模块-&lpar;元数据&amp&semi;IL代码&rpar;

    本文脉络图如下: 1.CLR(Common Language Runtime)公共语言运行时简介 (1).公共语言运行时是一种可由多种编程语言一起使用的"运行时". (2).CLR ...

  5. MYSQL数据库学习十一 多表数据记录查询

    11.1 关系数据操作 并(UNION):把具有相同字段数目和字段类型的表合并到一起. 笛卡尔积(CARTESIAN PRODUCT):没有连接条件表关系的返回结果.字段数=table1字段数+tab ...

  6. C&num;实现RSA加密与解密、签名与认证(转)

    一.RSA简介 RSA公钥加密算法是1977年由Ron Rivest.Adi Shamirh和LenAdleman在(美国麻省理工学院)开发的.RSA取名来自开发他们三者的名字.RSA是目前最有影响力 ...

  7. 一步步教你上架iOS APP

    注意,注意,注意:一定要设置Enable Xombie Objects为不勾选.在Edit Scheme中有一个Run,然后选择Diagnostics,然后取消勾选Enable Zombie Obje ...

  8. vue 编译原理 简介

    来源 tinycompile 关于vue的内部原理其实有很多个重要的部分,变化侦测,模板编译,virtualDOM,整体运行流程等. 之前写过一篇<深入浅出 - vue变化侦测原理> 讲了 ...

  9. index range scan&comma;index fast full scan&comma;index skip scan发生的条件

    源链接:https://blog.csdn.net/robinson1988/article/details/4980611 index range scan(索引范围扫描): 1.对于unique ...

  10. 【51nod】1851 俄罗斯方块

    题解 最近一遇到神仙题一卡就好久--做点水题滋养一下自己吧= = 显然我们发现放一个方块的奇偶性不会改变,所以格子如果黑格子是奇数,那么就是No 我们发现每个2 × 3的方格里的2 × 1的黑格子都可 ...