您的位置:首页 > 其它

MapReduce简介以及F#的实现

2010-03-04 11:13 232 查看
MapReduce是一个最先由Google提出的分布式编程模型,用于大规模数据集(大于1TB)的并行运算。概念"Map"和"Reduce",以及他们的主要思想,都是从函数式编程语言里借来的,还有从矢量编程语言里借来的特性。它极大地方便了编程人员在不会分布式并行编程的情况下,将自己的程序运行在分布式系统上。

对于大数据量的计算,通常采用的处理手法就是并行计算,对许多开发人员来说,并行计算还是一个比较遥远的东西。MapReduce它就是一种简化了的并行计算的编程模型,它让那些没有多少并行计算经验的开发人员也可以开发并行应用。在我看来,这也就是MapReduce的价值所在,通过简化编程模型,降低了开发并行应用的入门门槛。相对于现在普通的开发而言,并行计算需要更多的专业知识,有了MapReduce,并行计算就可以得到更广泛的应用。
MapReduce的名字源于这个模型中的两项核心操作:Map和 Reduce。MpaReduce的Map和Reduce过程都定义了数据结构(key,value)对,Map操作在一个数据域中用一种类型表达一个数据对,然后在另一个不同的域中返回一个数据队列:
Map(k1,v1) -> list(k2,v2)
Map过程并行应用于每一个输入的数据集,每一次调用都会产生一个(k2,v2)的队列。然后,MapReduce构架收集输出队列中有相同key的数据对,把它们聚集在一起,因为构建了一个不同key的数据对集合。
Reduce方法应用于上面产生的每一个数据对集合,从而产生相同域中的数据集合:
Reduce(k2,list(v2)) -> list(v3)
每一个被调用的Reduce方法产生一个v3数据集或者一个空集,这里也可以返回多个value。所有返回的调用结果组成一个结果队列。
MapReduce用在非常广泛的应用程序中,从最简单的统计一篇文章中所有单词出现次数到分布grep、分布排序、web访问日志分析,反向索引构建等。我们可以用多种不同的方式来实现多种不同语意的映射-归并算法,这个算法与其说是一个特定的算法,还不如说是一个算法族。为了更好的理解这一模型,下面利用微软最新推出的函数式编程语言F#来构建一个简单统计单词出现次数的MapReduce。
F# MapReduce函数原型:
let map_reduce
(m:'k1 -> 'v1 -> seq<'k2 * 'v2>)
(r:'k2 -> seq<'v2> -> 'v3 option)
: Map<'k1, 'v1> -> Map<'k2, 'v3> =
map_per_key >> group_by_key >> reduce_per_key
map_reduce 函数接收两个函数:m和r函数。m是映射函数,它的任务是接收k1/v1对,并且产生k2/v2队列;r是归并函数,当所有的映射函数都退出的时候,归并函数要负责针对每一个键,将它对应的所有值合并在一起。泛型v3是一个累加器,它的初始值是None。
Map<'k1, 'v1> -> Map<'k2, 'v3>说明map_reduce函数的参数接收和返回类型。除此之外,重要的要理解后面的三个函数map_per_key、group_by_key、reduce_per_key,这是map_reduce中的精髓。
map_per_key函数任务是把map转换成seq,然后映射到m函数进行计算,返回的值利用Seq.concat来加入以前统计的结果中;group_by_key函数的任务是统计每个key/value值,把相同key的值合并在一起,返回一个新的key/value中间值队列;reduce_per_key函数的作用是把group_by_key组成的中间值归并,把每个key中的value进行统计。
一个简单的例子:s = seq[("s",1);("s",1);("a",1)],应用group_by_key之后就变成了map[("s",seq[1;1]);("a",seq[1])];再应用了reduce_per_key就变成了map[("s",2);("a",1)]。
下面分别是三个函数的代码实现:
let map_per_key : Map<'k1, 'v1> -> seq<('k2 * 'v2)> =
Map.toSeq >> Seq.map (Tuple.uncurry m) >> Seq.concat

let group_by_key (l:seq<('k2 * 'v2)>) : Map<'k2,seq<'v2>> =
let insert d (k2, v2) = Map.insert_with Seq.append k2 (seq [v2]) d
let func (f:Map<'a, seq<'b>> -> Map<'a, seq<'b>>) (c:'a * 'b)
: (Map<'a, seq<'b>> -> Map<'a, seq<'b>>) =
fun x -> f(insert x c)
(Seq.fold func (fun x -> x) l) Map.empty

let reduce_per_key : Map<'k2, seq<'v2>> -> Map<'k2,'v3> =
let unSome k (Some v) = v
let isSome k = function
| Some _ -> true
| None -> false
Map.map r >> Map.filter isSome >> Map.map unSome
到目前为止,已经把map_reduce函数介绍完了,下面我们利用map_reduce来做一个单词出现次数的统计程序,下面的代码定义了映射函数m和归并函数r,同时调用map_reduce函数:
let word_occurrence_count : Map<string, string> -> Map<string, int> =
let m = const (String.words >> Seq.map(fun s -> (s, 1)))
let r = const (Seq.sum >> Some)
map_reduce m r

[<EntryPoint>]
let main(args:string array) =
printfn "%A"
(word_occurrence_count
(Map.empty
|> Map.add "doc1" "appreciate the unfold"
|> Map.add "doc2" "fold the fold the unfold"))
System.Console.ReadKey(true) |> ignore
0

当我们执行程序后,显示的结果是:
map [("appreciate", 1); ("fold", 2); ("the", 3); ("unfold", 2)]

在上面简单的例子中,没有使用到并行计算,有兴趣的朋友可以自己做实现
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐