Ruby实现Mongo MapReduce初探

MapReduce是一种计算模型,简单的说就是将大批量的工作(数据)分解(MAP)执行,然后再将结果合并成最终结果(REDUCE)。这样做的好处是可以在任务被分解后,可以通过大量机器进行并行计算,减少整个操作的时间。下面以MongoDB MapReduce为例说明:

##计算一个标签系统中每个标签出现的次数通过ruby实现:

require 'mongo'
class MongoMapReduce
	def generate_mr
		mongodb = init_mongo
		coll = mongodb.collection("things")
		map = "function(){
			this.tags.forEach(
			   function(z){
					emit( z , { count : 1 } );
				}
			);
		};"
		reduce = "function( key , values ){
			var total = 0;
			for ( var i=0; i<values.length; i++ )
				total += values[i].count;
			return { count : total };
		};"
		m = BSON::Code.new(map)
		r = BSON::Code.new(reduce)
		output = coll.map_reduce(m, r, { :out => "myresult" })
		puts output.stats
		mongodb.collection("myresult").find().each{|row| puts row.inspect }
		output.drop
	end
	def init_mongo
		mongodb = Mongo::Connection.new("localhost", 27017).db("test")
		#coll = mongodb.collection("things")
		#init_data coll
	end
	def init_data coll
		doc = {"_id" =>1,  "tags" => ['dog', 'cat']}
		doc2 = {"_id"=>2, "tags" => ['cat']}
		doc3 = {"_id"=>3, "tags" => ['mouse', 'cat', 'dog']}
		doc4 = {"_id"=>4, "tags" => []}
		coll.insert(doc)
		coll.insert(doc2)
		coll.insert(doc3)
		coll.insert(doc4)
		coll.find().each {|row| puts row.inspect }
	end
end
MongoMapReduce.new.generate_mr

##如需要运行以上代码还要安装mongo/bson/bson_ext:

$ gem install mongo
$ gem install bson
$ gem install bson_ext

##从上面的代码我们可以看出map/reduce是由符合js语法的函数组成,我们可以这样理解,当你在所有需要计算的行执行完了map函数,你就得到了一组key-values对。基本key是emit中的key,values是每次emit函数的第二个参数组成的集合。

##我们必须了解这一机制会要求我们遵守的原则,那就是当我们书写Map函数时,emit的第二个参数形式是我们的Reduce函数的第二个参数,而Reduce函数的返回值,可能会作为新的输入参数再次执行Reduce操作,所以Reduce函数的返回值也需要和Reduce函数的第二个参数结构一致。

##当我们的key-values中的values集合过大,会被再切分成很多个小的key-values块,然后分别执行Reduce函数,再将多个块的结果组合成一个新的集合,作为Reduce函数的第二个参数,继续Reducer操作。可以预见,如果我们初始的values非常大,可能还会对第一次分块计算后组成的集合再次Reduce。

具体的mongodb MapReduce内容可以参考[官方网站][1] [1]: http://www.mongodb.org/display/DOCS/MapReduce#MapReduce-ShellExample2 “mapreduce”

177 Words 22 December 2011 Suzhou, China