MIT6.824中 MapReduce 的示例 mrsequential.go
任务
mrsequential.go 这是一个同步代码,也就是说一个进程就完成了整个计算任务。
首先要明确 MapReduce 的目标是计算出各个单词一共出现了多少次。
代码思路
指明数据文件和功能函数:
首先呢,数据来源于项目中的文件,所以需要将文件传入给程序:
go run -race mrsequential.go wc.so pg*.txt
上述命令中包含了两个参数:一个是 pg*.txt ,这个指定的就是数据文件(待会就是要统计这里面的各个单词的出现次数);至于另一个参数wc.so,这个可以理解为一个集成插件吧(主要是为我们提供 map 函数和 reduce 函数的具体实现的)
// os.Args[1] 就是 wc.so
// 经过这一步,可以得到对应的 map 函数和 reduce 函数
mapf, reducef := loadPlugin(os.Args[1])
Map任务: 在这之前已经完成了所有基本工作的准备了,所以这里会正式进行数据统计。
- 遍历各个数据文件,获取文件内容
- 将内容作为入参传给 map 函数
- map 函数处理得到,形如[{A,1},{A,1}{b,1}]
kva := mapf(filename, string(content))
排序: 这一步是位于 Map 和 Reduce 中间的步骤,它所做的工作就是将 map 函数返回的结果根据 key 排序;这样做主要的目的就是为了方便后序 Reduce 阶段的统计
sort.Sort(ByKey(intermediate))
Reduce 任务: 这个阶段的任务就是要将 [{A,1},{A,1}{b,1}] 变成 [{A,2},{b,1}]
下面我们来看看它的具体做法:
- 将 map 返回的结果 [{A,1},{A,1}{b,1}] ,变成 [A,A,b]
- 将 [A,A,b] 交给 reduce 函数处理,得到 A:2, b:1
- 将 A:2, b:1 写入文件
i := 0
// intermediate [{"key","value"},{"key", "value"}]
for i < len(intermediate) {
j := i + 1
// 找到所有相同 key 的键值对
for j < len(intermediate) && intermediate[j].Key == intermediate[i].Key {
j++
}
values := []string{}
for k := i; k < j; k++ {
// values ["value","value"]
values = append(values, intermediate[k].Value)
}
// 统计出 key 对应的个数有多少个
output := reducef(intermediate[i].Key, values)
// this is the correct format for each line of Reduce output.
fmt.Fprintf(ofile, "%v %v\n", intermediate[i].Key, output)
i = j
}