本文永久链接地址:https://www.askmac.cn/archives/mongodb-aggregation.html
第10回 MongoDB中的aggregation 聚集处理
MongoDB中的aggregation 聚集处理的概要
一般而言,在NoSQL的程序中,没有RDB的SQL这种Group以及Sum函数等聚集功能。要执行聚集的话,需要在应用上独立地写代码。
但是,MongoDB的开发方针是是一边维持NoSQL的性能,一边实装类似于RDB的功能,关于聚集功能早就实装了。在MongoDB中执行聚集处理的方法有三种。
- Aggregation Framework
SQL中提供Group By语句以及Sum函数。可以从Mongo Shell中与查询一样实施。一部分的处理($group与$sort)就对应sharding,用各shard进行处理。
- MongoDB的Map/Reduce功能
独立定义Map函数/Reduce函数,执行聚集处理。在Aggregation Framework中无法做到的复杂的聚集处理,使用这种方法。因为对应sharding,所以可以执行分散处理。
- 与其他聚集处理软件/框架的合作
为了执行大规模的聚集处理,也可以与其他聚集处理软件/框架合作。在这次的文章中,我将介绍与Hadoop的合作。
那么马上让我们来使用Aggregation Framework,来试着实际操作处理吧。
Aggregation Framework
首先来准备要聚集的数据。这次使用假设的网页服务器访问日志数据。将下述样本数据保存在MongoDB中。
db.httplogs.insert({"url_path":"/", "status":"200"}); db.httplogs.insert({"url_path":"/", "status":"200"}); db.httplogs.insert({"url_path":"/", "status":"500"}); db.httplogs.insert({"url_path":"/", "status":"500"}); db.httplogs.insert({"url_path":"/top", "status":"200"}); db.httplogs.insert({"url_path":"/top", "status":"200"}); db.httplogs.insert({"url_path":"/top", "status":"404"}); db.httplogs.insert({"url_path":"/user", "status":"200"}); db.httplogs.insert({"url_path":"/user", "status":"500"}); db.httplogs.insert({"url_path":"/user", "status":"500"});
用Aggregation Framework来聚集
那么让我们来试着执行Aggregation Framework。通过使用之前保存的访问日志的数据,首先让我们来聚集每个URL的访问数吧。在此我们应该无视HTTP步骤代码。利用HTTP步骤代码的复数key聚集,我将在之后介绍。
将每个URL的访问数的聚集用Aggregation Framework实施的话,请用以下代码。
db.httplogs.aggregate ( { $group : { "_id" : "$url_path", "count" : { "$sum" : 1 } } });
请试着执行。
db.httplogs.aggregate ( { $group : { "_id" : "$url_path", "count" : { "$sum" : 1 } } });db.httplogs.aggregate ( { $group : { "_id" : "$url_path", "count" : { "$sum" : 1 } } }); { "result" : [ { "_id" : "/user", "count" : 3 }, { "_id" : "/top", "count" : 3 }, { "_id" : "/", "count" : 4 } ], "ok" : 1 }
实现了聚集。
之后让我们将URL以及HTTP步骤代码作为key来进行聚集吧。要用多个key进行聚集的话,$group operator的”_id”的值如下所示进行修正。
db.httplogs.aggregate( { $group : { "_id" : { "url_path" : "$url_path", "status" :"$status" }, "count" : { "$sum" : 1 } } });
试着执行。
> db.httplogs.aggregate( { $group : { "_id" : { "url_path" : "$url_path", "status" :"$status" }, "count" : { "$sum" : 1 } } });db.httplogs.aggregate( { $group : { "_id" : { "url_path" : "$url_path", "status" :"$status" }, "count" : { "$sum" : 1 } } }); { "result" : [ { "_id" : { "url_path" : "/user", "status" : "500" }, "count" : 2 }, { "_id" : { "url_path" : "/user", "status" : "200" }, "count" : 1 }, { "_id" : { "url_path" : "/top", "status" : "200" }, "count" : 2 }, { "_id" : { "url_path" : "/", "status" : "500" }, "count" : 2 }, { "_id" : { "url_path" : "/top", "status" : "404" }, "count" : 1 }, { "_id" : { "url_path" : "/", "status" : "200" }, "count" : 2 } ], "ok" : 1 }
在此也完成了聚集。
可以用Aggregation Framework做到的事
这样,如果是这样单纯的聚集,可以用Aggregation Framework来实施。
mongodb中也有Aggregation Framework中对于聚集非常方便的operator,可以组合起来使用。
例如如下所示,我们考虑使用求测试的平均分的SQL。
List1 求测试平均分的SQL
SELECT name as '_id', AVG(score) as 'average' FROM scores WHERE year = 'junior' GROUP BY = name
用Aggregation Framework表现上述SQL的话就如下所示。
List2 求测试平均分的Aggregation Framework
db.scores.aggregate( { $match : { "year" : "junior" } }, { $project : { "name" : 1, "score" : 1 } }, { $group : { "_id" : "$name", "average" : { "$avg" : "$score" } } });
这样将operator作为Filter一样使用,通过执行Pipeline处理,使其实现更加高度的聚集处理。(参照图1)。
图1 Aggregation Framework的Pipeline处理
关于其他的operator,在官方手册中,请参考SQL以及Aggregation Framework的Mapping表,我们将介绍一部分。
SQL | Aggregation operator |
WHERE | $match |
GROUP BY | $group |
HAVING | $match |
SELECT | $project |
ORDER BY | $sort |
LIMIT | $limit |
SUM() | $sum |
COUNT() | $sum |
官方手册中也写了其他对应各SQL的样本代码。
在上述operator中可能的范围可以用Aggregation Framework进行聚集。要执行以上复杂处理的情况下,可以使用Map/Reduce功能来定义map函数以及Reduce函数来实施。
那么让我们来试着用Map/Reduce进行聚集吧。
MongoDB的Map/Reduce
在此将刚刚用Aggregation Framework执行的聚集,这次再此使用Map/Reduce进行聚集。但这需要Map/Reduce中用JavaScript记述的map函数以及Reduce函数,我们来准备吧。
准备map 函数
Map函数大致来说,就是为了在Reduce函数中使用的,制成key以及value的处理。Map函数中还无法执行聚集处理。
在key之中,指定Grouping的key。与之前一样,首先聚集每个URL的访问数,所以无视HTTP步骤代码。HTTP步骤代码的利用的多个key的聚集我将在之后介绍。
为了在map函数内部制成key以及value,使用emit函数。在第一引数中记述key,在第二引数中记述聚集方法。在此,指定第一引数为this.url_path,第二引数为{count : 1}。
> map = function() { emit(this.url_path, {count: 1});}
Reduce函数的准备
在Reduce函数中,在用map函数内的emit函数指定的key 被grouping的状态下,这次为了数每个URL访问数,记述添加了count的处理。
reduce = function(key, values) { var count = 0; values.forEach(function(v) { count += v['count']; }); return {count: count};}
用map/Reduce进行聚集
作成了map函数与reduce函数的话,那么就来试着进行聚集吧。在聚集之中,聚集的collection的mapReduce函数的引数中第一引数中map函数,在第2引数中传送reduce函数。第3引数传送选项。通过指定{out: {inline:1}},结果就能在控制台中数据。那么让我们来试着实行mapReduce函数吧。
db.httplogs.mapReduce( map, reduce, {out: {inline:1}} ); { "results" : [ { "_id" : "/", "value" : { "count" : 4 } }, { "_id" : "/top", "value" : { "count" : 3 } }, { "_id" : "/user", "value" : { "count" : 3 } } ], "timeMillis" : 651, "counts" : { "input" : 10, "emit" : 10, "reduce" : 3, "output" : 3 }, "ok" : 1, }
可以确认URL访问数的合计是用count来进行聚集的。
接着就来试着将URL以及HTTP步骤代码来作为key来进行聚集吧。
※)
在选项中,可以保存结果collection的指定以及最后实施的finalize函数。详细请参考官方手册。
用多个key进行聚集
为了用复数key进行聚集,用hash指定emit函数的第一引数。修正map函数。
为了将URL以及HTTP步骤代码作为key,将第一引数如下所示进行变更。
> map = function() { emit({url_path: this.url_path, status: this.status}, {count: 1});}
Reduce函数如刚才那样就好了。让我们来试着执行mapReduce函数吧。
db.httplogs.mapReduce( map, reduce, {out: {inline:1}} ); db.httplogs.mapReduce( map, reduce, {out: {inline:1}} ); { "results" : [ { "_id" : "/", "value" : { "count" : 4 } }, { "_id" : "/top", "value" : { "count" : 3 } }, { "_id" : "/user", "value" : { "count" : 3 } } ], "timeMillis" : 33, "counts" : { "input" : 10, "emit" : 10, "reduce" : 3, "output" : 3 }, "ok" : 1, }
我们在此可以确认URL以及HTTP步骤代码都变成了聚集的key。
用map/reduce可以做到的事情
在Map/reduce之中,有个很大的特征是可以使用JavaScript自由地对map函数以及reduce函数进行定义。无法用Aggregation Framework做到,可以实装if等防御语句以及其他的JavaScript的函数的处理。
那么最后我将介绍与hadoop的合作。
与其他聚集处理Middle(Hadoop)的合作
试着与Hadoop组合来使用
通过将MongoDB与Hadoop组合使用,可以实现一边在数据store中使用MongoDB,一边通过Hadoop的强力分散功能所带来的高效并列处理,(参照图2)。活用作为Replica set、sharding、mongo shell等可变通的询问这样的数据store的mongoDB的优点,也可以使用Hadoop的分散处理功能,从而实现互取长处。
图2 MongoDB与Hadoop的组合
这次的文章仅仅介绍了MongoDB Hadoop Adapter的介绍,实际的使用方法请参考官方手册。
总结以及下次的主题
这次介绍了用MongoDB实施聚集的方法。在MongoDB中,有类似于询问来实施Aggregation Framework的功能以及可以自身记录处理内容的map/reduce功能。可以根据用途不同来分开使用。
对于近年需求逐年增加的大数据的聚集处理中,有通过与hadoop来组合进行处理的方法。MongoDB开发元的10gen提供Hadoop用的连接
下次 我将介绍使用MongoDB的使用。请大家多多期待!
Leave a Reply