MapReduce 是一种采用的是并行的思想用于处理大规模数据集的编程模型和处理框架,最初由 Google 提出,并在其大规模分布式数据处理系统中得到了广泛应用。它主要用于解决并行处理大规模数据集的问题
并行和并发的区别
并发(Concurrency)
- 并发是指系统能够同时处理多个任务,但并不一定同时执行。在并发中,任务之间可能会交替执行,每个任务只执行一小段时间,然后切换到另一个任务,以此类推。
并行(Parallelism):
- 并行是指系统真正地同时执行多个任务,这些任务在不同的处理器核心、多个计算单元或者多个计算机上同时运
完整的举例说明
reduce, err := mr.MapReduce(func(source chan<- interface{}) {
for _, pid := range pids {
source <- pid
}
}, func(item interface{}, writer mr.Writer[*modle.Product], cancel func(error)) {
i := item.(int64)
one, err := l.svcCtx.ProductModel.FindOne(ctx, i)
if err != nil {
cancel(err)
return
}
writer.Write(one)
}, func(pipe <-chan *modle.Product, writer mr.Writer[[]*modle.Product], cancel func(error)) {
var ps []*modle.Product
for item := range pipe {
ps = append(ps, item)
}
writer.Write(ps)
})
Map 阶段:在这个阶段,初始的输入数据被分割成若干个独立的数据块,然后并行地在多个处理节点上进行处理。在每个节点上,都会对输入的数据块进行相同的操作(通常是一个映射函数),产生一系列中间键值对
//参数类型为interface{}
//这里表示定义一个interface{}类型的管道
func(source chan<- interface{}) {
for _, pid := range pids {
source <- pid
}
}
Shuffle 阶段:
- 功能:Shuffle 阶段主要负责将中间数据重新分配和排序,以便进行后续的 Reduce 操作。在这个阶段,中间数据会按照键值对中的键重新分发到不同的节点上,并进行排序,使得具有相同键的数据可以聚合在一起。
- 处理:Shuffle 阶段通常会涉及大量的数据传输和网络通信,因为中间数据需要从 Map 任务的输出节点传输到 Reduce 任务的输入节点。在这个过程中,可能会发生大量的数据传输和数据拷贝操作,需要消耗大量的带宽和网络资源。
//参数一为第一阶段的返回类型,参数二为该阶段的返回类型,参数三为错误
func(item interface{}, writer mr.Writer[*modle.Product], cancel func(error)) {
i := item.(int64)
//对数据进行初步处理
one, err := l.svcCtx.ProductModel.FindOne(ctx, i)
if err != nil {
cancel(err)
return
}
//对数据进行返回
writer.Write(one)
}
Reduce 阶段:
- 功能:Reduce 阶段主要负责将相同键的中间数据聚合和归约,生成最终的处理结果。在这个阶段,每个 Reduce 任务会接收到一个或多个键的一组中间数据,并执行一个归约操作,将这些数据聚合成最终的输出结果。
- 处理:Reduce 阶段的处理通常是CPU密集型的,因为它需要执行复杂的归约函数来处理相同键的中间数据。这个阶段通常是并行执行的,每个 Reduce 任务独立地处理不同的键,因此可以利用多个处理器核心来提高处理性能
//参数一为上阶段的返回类型,参数二返回类型,参数三为错误
func(pipe <-chan *modle.Product, writer mr.Writer[[]*modle.Product], cancel func(error)) {
var ps []*modle.Product
for item := range pipe {
ps = append(ps, item)
}
writer.Write(ps)