`
yidongkaifa
  • 浏览: 4064681 次
文章分类
社区版块
存档分类
最新评论

mongodb的mapreduce

 
阅读更多

在功能点统计中,我是用mongodb所提供的mapreduce功能所实现的。关于mongodb,其实并没有多少可说的。简单的概述一下,它其实不过是一个可以存放很多json的仓库,只不过它对于json做了优化,使用二进制存储,改了个名叫做bson,这不过是很多年前就有的面向对象数据库的新的实现。至于性能,也可以简单的认为它自带了一个缓存,与硬盘做异步同步。这些其实都是很无趣的东西,真正有意思的,是它带了一个mapreduce工具(虽说,实现的真的很烂)。

mapreduce在我看来,是在互联网时代,一个程序员必须去了解的概念。在中文维基中,mapreduce被这样定义:“MapReduce是Google提出的一个软件架构,用于大规模数据集(大于1TB)的并行运算”。其实,这不准确,它其实是函数式语言中由来已久的一个概念。只不过,在这个互联网上信息爆炸的年代,google帮我们发现了它。

顾名思义,mapreduce分两部分,map和reduce。

map函数的定义为Map(k1,v1) → list(k2,v2)

reduce函数的定义Reduce(k2, list(v2)) → list(v3)

简单的描述一下,map接收一个list,通过某一个函数,把它变成另一个list。在这个过程中,对list中任一元素的变换都与其他操作无关。这样的函数天生具备了并行计算的能力。而reduce,它通过某种操作,具体来说也是通过一个函数,把接收到的list转变为一个简单的结果。reduce函数不是那么方便并行,所以通常mapreduce框架会把reduce操作集中到某个特定的节点进行。

看一下功能点统计中一段具体的mapreduce函数

var map =function () {

var a = new Date,

d = new Date(a.getFullYear(), a.getMonth(), a.getDate()),

e = this.auid;

this.features.forEach(function (b) {

var a = b.log.length,

c = 0;

b.log.forEach(function (a) {

a > d && c++

});

emit({

auid: e,

fid: b.id,

fname: b.name,

version: b.version

}, {

total: a,

today: c

})

})

}

在这段函数中,前面具体的逻辑操作不重要,重要的是后面的emit函数。这个函数由mongodb提供,在这个例子中,它将原始数据进行分割,分割结果是这样的结构

[{_id:{auid:xxx,fid:xxx,fname:xxx,version:xxx},value:[{total:xx,today:xxx}....]}]

换句话说,emit的作用就是根据第一个参数提供的属性,将原始数据分割为一个新的list

这里是reduce函数

var reduce = function (d, c) {

var a = {

total: 0,

today: 0

};

c.forEach(function (b) {

a.total += b.total;

a.today += b.today

});

return a

}

这个函数看起来就简单很多了,它要做的就是,接收上一个list中某个元素的value的值,这是一个小的list,把它变成一个对象输出。

从逻辑上可以看到,这个reduce对之前那个list的每个元素要执行一次,这个调度工作由mongodb执行。最终,这些输出结果合并到一起,变成新的list,成为我们的最终结果。

之前提到,我对mongodb的mapreduce其实非常不满。原因很多,首先它不能利用index,导致性能不高;其次,它对mapreduce所用到的数据有诸多限制;最重要的一点,其实是它根本没有很好的实现并行:在单机环境下,mongodb的mapreduce只能单线程运行,在分布式环境下有限制了很多有用的特性。这样看来,mongodb的mapreduce事实上只是起到了一个简单的group by作用,而不是一个真正最强有力的并行计算工具。在这里,我的观点是研究mapreduce的思想,远比研究mongodb的这个实现有价值的多。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics