MapReduce简介以及F#的实现

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

对于大数据量的计算,通常采用的处理手法就是并行计算,对许多开发人员来说,并行计算还是一个比较遥远的东西。MapReduce它就是一种简化了的并行计算的编程模型,它让那些没有多少并行计算经验的开发人员也可以开发并行应用。在我看来,这也就是MapReduce的价值所在,通过简化编程模型,降低了开发并行应用的入门门槛。相对于现在普通的开发而言,并行计算需要更多的专业知识,有了MapReduce,并行计算就可以得到更广泛的应用。

MapReduce的名字源于这个模型中的两项核心操作:Map和 ReduceMpaReduceMapReduce过程都定义了数据结构(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 函数接收两个函数:mr函数。m是映射函数,它的任务是接收k1/v1,并且产生k2/v2队列r是归并函数,当所有的映射函数都退出的时候,归并函数要负责针对每一个键,将它对应的所有值合并在一起。泛型v3是一个累加器,它的初始值是None

Map<'k1, 'v1> -> Map<'k2, 'v3>说明map_reduce函数的参数接收和返回类型。除此之外,重要的要理解后面的三个函数map_per_keygroup_by_keyreduce_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进行统计。

一个简单的例子:= 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)]

在上面简单的例子中,没有使用到并行计算,有兴趣的朋友可以自己做实现

 

你可能感兴趣的:(MapReduce简介以及F#的实现)