由于nodejs本身的限制,在程序中使用js进行大批量计算效率不高。而V8引擎自身对内存大小的限制(64位系统下1.4G),同样限制了数据规模。

因此,相对于从mongodb中抽出数据进行计算,在mongodb中利用聚合函数或者其他方法完成计算,避开nodejs自身限制的方案在可靠性和扩展性上都相对较为令人满意。

mongodb支持类似SQL中的聚合函数,虽然语法不通,不过基本原理类似。

mongodb自带的接口中,aggregate被用来实现聚合查询:

rec = db.LIBRARY.aggregate([{
$match: {
date: {
$gte: '2220832129000',
$lte: '3330918529000'
}
}
}, {
$group: {
_id: '$book_id',
maxScience: {
$max: '$shelf.science'
},
maxMath: {
$max: '$data.math'
},
avgAlgebra: {
$avg: '$data.algebra'
},
standardDeviation: {
$stdDevPop: '$data.score' // 标准差
},
allValues: {
$values: '$data.score' // 分组后的结果值数组
}
}
}])

在$group中可以利用各种运算符实现各种聚合运算。

如果目的是生成字段不多的简单数据结构,聚合运算几乎可以一步到位。

需要注意的是在没有格式转换的情况下,js对于字符串和数字的区分很模糊。如果对字符串变量使用max函数,出现的结果会是"999" > "1234"。如果mongodb内部数据格式不规范,可能得不到理想的结果。

对于复杂的计算,可以使用mapReduce方法。

mongodb的原生方法中包括了mapReduce方法,遍历所有符合query条件的数据,对于每一条抽取到的数据,通过map方法提出键值对,在收到的键发生变化时,将目前为止当前键的所有值存储在一个数组中。

对于reduce方法触发的时机,是在遍历结果集,键发生变化时触发,还是遍历结束后,对新的键值对遍历执行reduce方法,这个目前还不能确定。

传递给reduce方法的参数包括键和一个存储目前为止当前键的所有值的数组,reduce处理结束后,返回处理结果的统计结果,mapReduce过程结束。

mapReduce是个功能很强大的方法,目前我只能将接触到的内容记录下来,等待以后慢慢完善。

首先,是最核心的map方法和reduce方法。

※ map方法

处理query条件抽取的结果集,对于每一条mongodb的记录,都会执行一次map方法。

map方法一般不需要传入参数,在方法体重,this指向当前处理的记录,可以通过this.columnName方式调用记录中的字段。

map方法不需要返回值,在方法体中,调用内置的emit方法,以emit(key, val)的形式向mapReduce内存中传递一组键值对。键值对的格式很自由,即使在当前记录中不存在的变量,只要符合语法,同样可以正常emit传出。网上的例子里,大部分emit方法中的value值都是数字,或单变量,但其实emit传递的键值可以是复杂的json甚至某个外部方法,配合对应的reduce方法,可以实现更为复杂也更有效的功能。

※ reduce方法

reduce方法的调用时机目前还不确定,基础原理是接收两个参数,key和values。其中key是emit传出的键,values是经过整合后,集中在同一个数组中的相同key的值,即使只有一个值,values也是数组形式存储的。

在此过程之前,相同key的相同value并不会被合并,map reduce概念说明中提到的词频统计的实例,是在map reduce过程完成后才产生了被合并的结果,而在reduce方法真正执行之前,相同key的相同value应该会保持emit时的状态。

在reduce方法中,遍历values数组,根据emit方法中value对应的结构,对有共同特性的结果进行统一处理,最后返回结果,在mapReduce方法的返回值中,当前key对应的值就是reduce方法返回的结果。可以总结为mapReduce方法的返回结果就是 emit中的key: reduce中的返回值 形式的json格式键值对组。

在mongodb自己的js接口中,mapReduce方法的直接返回值是方法的结果统计:

> rec = db.DATA.mapReduce(m, r, {query:{time:{$gte:'1450832129000'}}, sort: {id:1}, out:"result"})
{
"result" : "result",
"timeMillis" : 50948,
"counts" : {
"input" : 490672,
"emit" : 490672,
"reduce" : 4931,
"output" : 26
},
"ok" : 1
}
>

使用这种接口,如果需要查看详细的计算结果就需要设置mapReduce方法的out参数。

out参数的值是字符串时,返回的结果中result属性就是该字符串的值,如果只需要查看统计结果,这样也就够了。

如果需要缓存计算结果,就需要将out参数的值设置为{<option>:"临时表名"}的json结构。mapReduce的计算结果会存在mongodb的临时表中,可以通过find方法查看。

在out参数值的json结构中,<option>定义的是对临时表中相同的key的处理方式,一共有三种:

replace 使用当前reduce结果替换临时表中存在的结果

merge 当前reduce结果的key在临时表中不存在时,将reduce结果存入临时表,如果存在相同key,直接使用当前key的值替换临时表中相同key的值

reduce 如果当前key在临时表中存在,则对当前结果和已存在结果进行map reduce,重新调用map方法与reduce方法,将结果整合。

这里需要注意的是,quey结果集较大时,可能出现对同一个key多次reduce的情况,这时需要设置out的整合方法为reduce,否则临时表中存储的将只是其中一次reduce的结果,而非所有记录的reduce处理结果。在这种情况下,最好保持reduce方法的返回值结构与map方法中emit的value结构相同,使后续mapReduce可以正常执行相同的map方法与reduce方法。

这次使用的并不是mongodb自己的js接口,而是mongoose,作为第三方mongodb插件,使用起来比原生的要相对方便一些。

mongoose的model.mapReduce方法接收两个参数:mapReduce结构设定对象、回调函数(因为是异步方法)

在mapReduce对象中,mapReduceObj.map指定map方法,mapReduceObj.reduce指定reduce方法,对于out参数,mongoose的mapReduce方法默认设置为{inline: 1},以js的json对象格式返回计算结果,默认对相同key的计算结果执行reduce方法,如果预期结果规模不大,就不需要像原生方法那样再设置临时表。

在map方法和reduce方法中如果需要使用外部的js变量,可以设置mapReduceObj.scope({key:外部变量,...}),在map或reduce方法中,直接使用定义好的key,就可以取到外部变量的值。

※ mapReduce的优化

对于处理大量数据的方法,在query后将结果集排序,尽量保证在执行map方法时相同的key连续出现,减少reduce的次数,可以显著提升mapReduce的效率。这里要注意,由于调用了sort,最好将key值涉及的mongodb字段加上联合或独立索引,避免mongodb引擎使用默认sort方法产生错误。

还可以通过外部变量,过滤emit传出的结果,只保留需要的数据,减少冗余计算。

如果使用的语言支持,还可以考虑多线程方式执行,mapReduce方法的limit参数与skip参数,可以很方便的将结果集分块。

另外,最近看蝴蝶书,里边对于递归的解释让我对mapReduce方法的实现有了些感想,mapReduce中,reduce方法使用了类似递归的概念,从结果看输入,就是将输入的数据集合根据键值的不同,分成若干块,对每一块再分别调用reduce方法,逐层递归,直到每一块的内容都是一条单独的记录,无法继续分割为止。相同的map&reduce方法的递归调用。

关于mongodb的mapReduce的更多相关文章

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

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

  2. MongoDb 用 mapreduce 统计留存率

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

  3. mongoDB实现MapReduce

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

  4. MongoDB:Map-Reduce

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

  5. MongoDB中mapReduce的使用

    MongoDB中mapReduce的使用 制作人:全心全意 mapReduce的功能和group by的功能类似,但比group by处理的数据量更大 使用示例: var map = function ...

  6. mongodb 聚合(Map-Reduce)

    介绍 Map-reduce 是一种数据处理范式,用于将大量数据压缩为有用的聚合结果.对于 map-reduce 操作,MongoDB 提供MapReduce数据库命令. MongoDB中的MapRed ...

  7. 在MongoDB的MapReduce上踩过的坑

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

  8. MongoDB进行MapReduce的数据类型

    有很长一段时间没更新博客了,因为最近都比较忙,今天算是有点空闲吧.本文主要是介绍MapReduce在MongoDB上的使用,它与sql的分组.聚集类似,也是先map分组,再用reduce统计,最后还可 ...

  9. mongoDB(3) mapReduce

    mapReduce是大数据的核心内容,但实际操作中别用这个,所谓的mapReduce分两步 1.map:将数据分别取出,Map函数调用emit(key,value)遍历集合中所有的记录,将key与va ...

  10. MongoDB下Map-Reduce使用简单翻译及示例

    目录 Map-Reduce JavaScript 函数 Map-Reduce 行为 一个简单的测试 原文地址https://docs.mongodb.com/manual/core/map-reduc ...

随机推荐

  1. 【[USACO12DEC]第一!First!】

    一个串不能成为第一的情况有两种 另外一个单词是它的前缀 在分配字母表大小关系的时候出现了矛盾的情况 第一种很好判断,一旦我们在一个单词没有匹配完之前遇到一个结束标志,那么就说明另外一个单词是它的前缀 ...

  2. 你真的了解现在的PHP吗?

    前段时间,公司的项目从PHP5.3升级到PHP7,现在项目里开始使用PHP7的一些新语法和特性.反观PHP的5.4.5.5.5.6版本,有点认知缺失的感觉.所以,决定看<Modern PHP&g ...

  3. tomcat快速部署War包操作

    Linux快速部署War包操作,暂时是最简单的操作 1.先关闭Tomcat /home/java/tomcat7/bin/shutdown.sh 2.进入War包存放目录(可以通过工具:SSH Sec ...

  4. git忽略文件留存

    ##ignore this file##/target/ .classpath.project.settings /.settings/ ##filter databfile.sln file##*. ...

  5. kettle maven 配置

    <properties> <kettle.version>6.1.0.4-225</kettle.version> </properties> < ...

  6. win7环境中使用notepad++配置python IDE

    1, 下载notepad++,并安装 http://notepad-plus-plus.org/download/v6.5.5.html 2, 下载python的win版本,并安装 https://w ...

  7. input输入框中list属性不能没有效果

    网页代码入下,就是出不来效果. 人物:zslsww 解决办法: 将option中的value属性去掉: 张山李四王五爪留 显示效果,问题得到解决.

  8. Matplotlib中中文不显示问题

    我们在使用jupter进行数据分析的时候,会接触到Matplotlib这个库,它是用来进行可视化数据分析的,在一个图中,我们常常会加入一些中文来进行说明.当我们加入中文的时候会出现下图所示的样子: 可 ...

  9. C#自定义异常

    继承自System.ApplicationException类,并使用Exception作为自定义异常类名的结尾 三个构造函数:一个无参构造函数:一个字符串参数的构造函数:一个字符串参数,一个内部异常 ...

  10. 【补】英语对IT工作者的重要性

    浅谈程序员的英语学习   作为在中国工作的程序员,不懂得英语似乎也不妨碍找到好工作,升职加薪.但程序员这个工种则稍有不同,因为程序,尤其是高级语言,基本上都是由英语和数字表达式构成的.英语对于程序员十 ...