在MongoDB的MapReduce上踩过的坑
太久没动这里,目前人生处于一个新的开始。这次博客的内容很久前就想更新上来,但是一直没找到合适的时间点(哈哈,其实就是懒),主要内容集中在使用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
转载于:https://www.cnblogs.com/lekko/p/3963418.html
在MongoDB的MapReduce上踩过的坑相关推荐
- 那些年我们在spark SQL上踩过的坑
做了一年延云YDB的开发,这一年在使用spark上真心踩了不少坑,总结一下,希望对大家有所帮助. spark 内存泄露 1.高并发情况下的内存泄露的具体表现 很遗憾,spark的设计架构并不是为了高并 ...
- 那些年,在nodejs上踩过的坑
原文:http://cnodejs.org/topic/4fc7789a8be5d070121141cd ----------------------------------------------- ...
- 搭建风控系统道路上踩过的坑合集
作者前言 从业近10年,大大小小参与了3家公司不同领域的风控系统的设计,从前到后把风控系统所有环节都细细的琢磨过,然而至今仍然感觉刚刚一只脚踏进门而已. 大多数人做的产品都是目的明确的,比如订单支付. ...
- flutter ios上踩的一个坑
在ios模拟器上调试的时候出现错误信息:The iOS Simulator deployment target is set to 4.3... 原来是因为ios 版本问题,于是找到资料是在 Podf ...
- soapui 乱码_在soapui上踩过的坑
资料来自网络 1.新建一个DataSource,选择Excel(需要Excel2003格式),选择行号,设置起始行结束行,设置规则,trim等 2.在返回结果的outline中,可以直接选定一个记录进 ...
- OpenCV在Android上踩过的坑
最近一个新项目需要用的到人脸检测,想着之前见过opencv,就在网上找了配置教程,结果一直无法成功,鉴于网上的基本都是330版本,想着可能比较老,去官网下载了最新版(那个下载速度啊,真的是-)建议可以 ...
- (转)Redis上踩过的一些坑-美团
上上周和同事(龙哥)参加了360组织的互联网技术训练营第三期,美团网的DBA负责人侯军伟给大家介绍了美团网在redis上踩得一些坑,讲的都是干货和坑. 分为5个部分: 一.周期性出现connect t ...
- Redis上踩过的一些坑-美团
上上周和同事(龙哥)参加了360组织的互联网技术训练营第三期,美团网的DBA负责人侯军伟给大家介绍了美团网在redis上踩得一些坑,讲的都是干货和坑. 分为5个部分: 一.周期性出现connect t ...
- 使用MongoDB的MapReduce
MapReduce是Google在2004年推出的一种软件框架,用于支持对计算机集群中的大数据集进行分布式计算. 您可以从此处阅读有关MapReduce的信息 . MongoDB是用C ++编写的面向 ...
最新文章
- github pages_使用GitHub Pages和Lighthouse增强您的开发人员产品组合
- 语义分割--FCN 算法中的一些细节--特征怎么融合
- MySQL Workbench
- Spring JDBC数据库连接池设置
- 中的实践 中兴_中兴通讯5G智慧治水业务在千岛湖畔下姜村成功实践
- 电话光端机的电话业务不通问题,该怎么去检查?
- poj 1502 MAPMaelstrom 单源最短路dijkstra
- [导入]《交互设计之路——让高科技产品回归人性》读书笔记(九)
- spring boot 注解
- 操作手册模板_挂蓝悬臂浇筑箱梁施工技术操作手册
- 我的Android进阶之旅------Android ListView优化详解
- jquery mini下载_【芒果tv湖南卫视直播】-芒果TV播放器下载v6.3.4 官方正式版
- CTFmisc常见音频隐写总结
- NRF24L01入门总结
- 看不看?这就是程序员996的真实内幕!
- Leetcode(934)——最短的桥
- 网易视频云:新一代列式存储格式Parquet
- kali linux 入侵教程,kali linux 入侵wordpress! wpscan工具使用方法!
- 如何将微信服务号改成订阅号?
- dp模式是什么意思_DP的完整形式是什么?
热门文章
- vs2005常用快捷键_包括代码自动缩进
- Netty工作笔记0056---Unpooled应用实例2
- [VC]基于对话框程序,自定义工具栏(支持真彩色图标,可添加文字)
- BCGControlBar的使用方法
- C语言和设计模式(之开篇)
- 一步一步写算法(之基数排序)
- java clone原理_cloneAble接口实现clone()原理
- php 通过ajax上传文件,php – 通过ajax上传文件
- python全局变量的声明和使用_python自学篇(第三章:函数)
- python中说_name_没有被定义_python – 为什么我得到这个NameError:名称’url_for’没有定义?...