MapReduce是聚合工具的明星!前面讨论的count、distinct、group能做到的,MapReduce都可以做!他是一个可以轻松并行化到多台服务器的聚合方法!他会拆分问题,将各个部分发送到不同的机器上执行,当所有机器都完成时,再把结果汇集起来形成最终完整的结果!
MapReduce在MongoDB中的使用通常有如下几个步骤:
1》 映射(map),将操作映射到集合中的每一个文档,这个操作在文档上执行后,要么没产生任何结果,要么产生一些键值对!
2》 洗牌(shuffle), 这是一个中间过程。上述映射会产生一些键值对,这个动作会将这些键值对按键分组,并将值组成列表设置到对应的键中。
3》 化简(reduce),把上述操作产生的值为列表的键值对化简为一个单值!这个键值对会被返回,他有可能还会参与下一轮的洗牌,化简操作。直到每个键的列表只有一个值为止。
使用MapReduce的代价是速度,group操作不是很快,MapReduce更慢!不能把MapReduce应用于实时系统中!要作为后台任务来运行MapReduce,其运行完毕后,会将结果保存在一个集合中,我们后期可以对这个结果集合进行实时操作!MapReduce比较复杂,我们通过几个例子先看看具体用法:
【例:找出集合中所有的键】
MongoDB没有模式,因此无法通过一个文档得知这个集合有多少个键,这里我们利用MapReduce来统计一个集合中键的个数和每个键出现的次数(这里没有考虑内嵌文档的键,可以通过调整map函数实现)。我们先看看我们需要统计的集合,每个文档中键都不一致(这里只是为了测试,实际中不要设计这样的集合):
1.
> db.testcol.find();
2.
{
"_id"
: ObjectId(
"501fadbee64fb552a4f6651e"
),
"x"
: 1 }
3.
{
"_id"
: ObjectId(
"50275d56e02ab93d5c5be7a3"
),
"name"
:
"abc"
,
"things"
: [
"plane"
,
"gun"
] }
4.
{
"_id"
: ObjectId(
"50275d6be02ab93d5c5be7a4"
),
"name"
:
"ddd"
,
"weapon"
:
"bomb"
}
5.
{
"_id"
: ObjectId(
"50275d7ce02ab93d5c5be7a5"
),
"nickname"
:
"viper"
,
"weapon"
:
"knife"
}
6.
>
定义映射环节(map)所需map函数:
01.
> map =
function
(){
02.
...
for
(
var
key
in
this
){
03.
... emit(key, {
"count"
: 1});
04.
... }
05.
... };
06.
function
() {
07.
for
(
var
key
in
this
) {
08.
emit(key, {count:1});
09.
}
10.
}
11.
>
map函数使用函数emit(系统提供)“返回”要处理的值,这里用emit将文档的某个键的计数返回({“count” : 1})。我们这里需要为每个键单独计数,所以要为每个键分别调用一次emit。this代表目前进入map函数中的文档!
定义化简环节(reduce)所需reduce函数:
01.
> reduce =
function
(key, emits){
02.
...
var
total = 0;
03.
...
for
(
var
i
in
emits) {
04.
... total += emits.count;
05.
... }
06.
...
return
{
"count"
: total};
07.
... };
08.
function
(key, emits) {
09.
var
total = 0;
10.
for
(
var
i
in
emits) {
11.
total += emits.count;
12.
}
13.
return
{count:total};
14.
}
15.
>
通过map函数会产生很多{"count" : 1}这样的文档,且每一个与一个键关联!这种由一个或多个{“count” :1}文档组成的数组(由洗牌阶段生成),会传递给reduce函数,作为reduce函数的第二个参数!reduce函数要能够被反复调用,因此reduce函数返回的值必须可以作为其第二个参数的一个元素!如上例!
运行数据库命令,调用MapReduce过程:
01.
> mr = db.runCommand({
"mapreduce"
:
"testcol"
,
"map"
: map,
"reduce"
: reduce,
"out"
:
"testcolColumns"
});
02.
{
03.
"result"
:
"testcolColumns"
,
04.
"timeMillis"
: 93,
05.
"counts"
: {
06.
"input"
: 4,
07.
"emit"
: 11,
08.
"reduce"
: 3,
09.
"output"
: 6
10.
},
11.
"ok"
: 1
12.
}
13.
>
运行命令时, 键“mapreduce” 指定集合名称,键“map”指定映射函数, 键“reduce”指定化简函数,“out”指定最后输出的集合名称!在我所使用2.0.6版本的MongoDB,键“out”必须指明!
我们运行后,返回的文档,其中键“counts”为一个内嵌文档,我们先说一下这个内嵌文档中各个键的含义:
1》 “input” : 在整个过程发送到“map”函数的文档个数,即“map”函数执行的次数
2》 “emit” : 在整个过程,“emit”函数执行的次数
3》 “reduce” : 在整个过程,“reduce”函数执行的次数
4》 “output” : 最终在目标集合中生成的文档数量
我们查看一下最终生成的目标集合:
1.
> db.testcolColumns.find();
2.
{
"_id"
:
"_id"
,
"value"
: {
"count"
: 4 } }
3.
{
"_id"
:
"name"
,
"value"
: {
"count"
: 2 } }
4.
{
"_id"
:
"nickname"
,
"value"
: {
"count"
: 1 } }
5.
{
"_id"
:
"things"
,
"value"
: {
"count"
: 1 } }
6.
{
"_id"
:
"weapon"
,
"value"
: {
"count"
: 2 } }
7.
{
"_id"
:
"x"
,
"value"
: {
"count"
: 1 } }
8.
>
其中,键“_id”的值为原集合中键的名称,键“value” 指明这个键在原集合中出现的次数!
【例:网页分类】
我们有这样一个网站,用户可以在其上提交他们喜爱的链接url,并且提交者可以为这个url添加一些标签,作为主题,其他用户可以为这条信息打分。我们有一个集合,收集了这些信息,然后我们需要看看哪种主题最为热门,热门程度由最新打分日期和所给分数共同决定,我们先看一下这个集合:
01.
> db.urlvote.find();
02.
{
"_id"
: ObjectId(
"502767f1e02ab93d5c5be7a6"
),
"date"
: ISODate(
"2012-08-12T08:23:13.292Z"
),
"score"
: 10,
"tags"
: [
"it tech"
,
"program"
],
"url"
:
03.
"www.csdn.net"
}
04.
{
"_id"
: ObjectId(
"50276810e02ab93d5c5be7a7"
),
"date"
: ISODate(
"2012-08-12T08:23:44.836Z"
),
"score"
: 3,
"tags"
: [
"search engine"
],
"url"
: "www.
05.
baidu.com" }
06.
{
"_id"
: ObjectId(
"5027683be02ab93d5c5be7a8"
),
"date"
: ISODate(
"2012-08-12T08:24:27.392Z"
),
"score"
: 5,
"tags"
: [
"search engine"
,
"news"
],
"url"
07.
:
"www.sina.com.cn"
}
08.
{
"_id"
: ObjectId(
"5027685de02ab93d5c5be7a9"
),
"date"
: ISODate(
"2012-08-12T08:25:01.588Z"
),
"score"
: 8,
"tags"
: [
"it tech"
,
"java"
],
"url"
: "ww
09.
w.javaeye.com" }
10.
{
"_id"
: ObjectId(
"5027687ae02ab93d5c5be7aa"
),
"date"
: ISODate(
"2012-08-12T08:25:30.354Z"
),
"score"
: 10,
"tags"
: [
"search engine"
],
"url"
: "www
11.
.google.com" }
12.
>
我们的map函数为:
01.
> map =
function
(){
02.
...
for
(
var
i
in
this
.tags){
03.
...
var
recency = 1/(
new
Date() -
this
.date);
04.
...
var
score = recency *
this
.score;
05.
... emit(
this
.tags, {
"urls"
:[
this
.url],
"score"
:
this
.score});
06.
... }
07.
... };
08.
function
() {
09.
for
(
var
i
in
this
.tags) {
10.
var
recency = 1 / (
new
Date -
this
.date);
11.
var
score = recency *
this
.score;
12.
emit(
this
.tags, {urls:[
this
.url], score:
this
.score});
13.
}
14.
}
reduce函数为:
01.
> reduce =
function
(key, emits) {
02.
...
var
total = {
"urls"
:[],
"score"
:0};
03.
...
for
(
var
i
in
emits) {
04.
... emits.urls.forEach(
function
(url) {
05.
... total.urls.push(url);
06.
... });
07.
... total.score += emits.score;
08.
... }
09.
...
return
total;
10.
... };
11.
function
(key, emits) {
12.
var
total = {urls:[], score:0};
13.
for
(
var
i
in
emits) {
14.
emits.urls.forEach(
function
(url) {total.urls.push(url);});
15.
total.score += emits.score;
16.
}
17.
return
total;
18.
}
19.
>
在reduce函数中,我们涉及到了数组的几个方法,forEach(function)和push,这都是javascript中提供的,可以学着用一下!再稍微说一下,javascript中通过for去遍历数组for(var i in array),这个i是数组的索引,从0开始,通过for去遍历一个json对象({key1:val1,key2:val2 .....}),for(var i in jsonobj),这个i是json对象中的键!
开始执行MapReduce过程:
01.
> db.runCommand({
"mapreduce"
:
"urlvote"
,
"map"
:map,
"reduce"
:reduce,
"out"
:
"urlvoteresult"
});
02.
{
03.
"result"
:
"urlvoteresult"
,
04.
"timeMillis"
: 0,
05.
"counts"
: {
06.
"input"
: 5,
07.
"emit"
: 8,
08.
"reduce"
: 2,
09.
"output"
: 5
10.
},
11.
"ok"
: 1
12.
}
查看结果集合中的文档为:
1.
> db.urlvoteresult.find();
2.
{
"_id"
:
"it tech"
,
"value"
: {
"urls"
: [
"www.csdn.net"
,
"www.javaeye.com"
],
"score"
: 18 } }
3.
{
"_id"
:
"java"
,
"value"
: {
"urls"
: [
"www.javaeye.com"
],
"score"
: 8 } }
4.
{
"_id"
:
"news"
,
"value"
: {
"urls"
: [
"www.sina.com.cn"
],
"score"
: 5 } }
5.
{
"_id"
:
"program"
,
"value"
: {
"urls"
: [
"www.csdn.net"
],
"score"
: 10 } }
6.
{
"_id"
:
"search engine"
,
"value"
: {
"urls"
: [
"www.baidu.com"
,
"www.sina.com.cn"
,
"www.google.com"
],
"score"
: 18 } }
7.
>
以上就是MapReduce在MongoDB中的两个例子,使用MapReduce的关键还是在于知道哪些情况适合于这种方式去解决,并且知道如何定义map和reduce函数。
上面我们在运行mapreduce命令时,只是涉及了"mapreduce" ,“map”,“reduce”,“out”键,我们还有如下键可用:
1》 “verbose” 布尔,是否产生详细的服务器端日志
2》 “query” 文档,在将集合中的文档发往map函数前,先用这个对文档进行过滤
3》 “sort” 文档, 在讲集合中的文档发往map函数前,先对文档排序,可与“limit”结合使用
4》 “limit” 整数, 取集合前部多少个文档发往map函数
后3者,可以减少发往map函数的文档数量,这个可以提升MapReduce的效率!如果我们事先确定只需对部分文档进行MapReduce操作,我们要果断使用这3个键!
相关推荐
学习MongoDB不仅需要理解其基本的数据操作和管理,还需要了解其高级特性、集群部署、索引机制、日志系统、数据备份与恢复等方面的知识。接下来将基于提供的文件内容详细展开介绍MongoDB的相关知识点。 一、入门 1. ...
MongoDB 学习笔记 本文档是 MongoDB 学习笔记,涵盖了 NoSQL、Cache、运行平台等多个方面的知识点。 一、NoSQL 数据库 MongoDB MongoDB 是一个强大、灵活、可扩展性好的文档数据存储器,可以完成大部分关系数据库...
MongoDB的MapReduce是一个强大的工具,它允许开发者处理和聚合大量数据。MapReduce基于一种分布式计算模型,将大规模数据处理任务分解为两步:Map(映射)和Reduce(归约)。在这个过程中,MongoDB首先应用Map函数...
MongoDB是一种流行的开源文档数据库系统,属于...这些笔记和教程涵盖了MongoDB的基础知识到高级用法,对于学习和理解MongoDB的操作和特性非常有帮助。通过阅读提供的文档,可以深入学习MongoDB的使用技巧和最佳实践。
7. **MapReduce**:用于批量处理和聚合操作,Map函数遍历集合,Reduce函数处理结果,使用JavaScript编写。 8. **GridFS**:内置功能,用于存储大量小文件。 9. **服务器端脚本**:MongoDB允许在服务器端执行...
其他说明:本课程资料来源于大数据专业学生的学习笔记,内容涵盖了大量实操练习案例,有助于加深理解和记忆知识点。建议在实验环境搭建好后再进行学习,并亲自尝试每一个例子以获得更好的学习效果。
11. MapReduce: MongoDB支持MapReduce函数,用于处理大数据分析,通过映射(map)和化简(reduce)过程对数据进行聚合计算。 以上只是MongoDB基础操作的一部分,实际使用中还有许多其他高级特性和用法,如聚合...
大数据技术学习笔记1 是一份关于大数据技术的学习笔记,涵盖了大数据技术的基本概念、Hadoop 生态系统、MapReduce 算法、Spark 框架、分布式计算平台等多个方面。 Hadoop 生态系统 Hadoop 是一个基于 Java 的开源...
17. **MongoDB**: 高性能、无模式的NoSQL数据库,适合存储非结构化和半结构化数据。 18. **Saiku**: 商业智能和数据分析工具,提供多维数据集的查询和报表创建。 19. **Kerberos**: 安全认证协议,用于网络服务的...