这篇博文主要是对 2021 VLDB Summer School Lab0 的一个总结
这个lab与MIT 6.824 的 lab1 相似,个人感觉比MIT 6.824 的 lab1 要稍微简单些,更容易上手。通过这个lab,可以学习到一些 Golang 的基础知识并对分布式系统有一个基础的了解,我做下来感觉文档写得十分完善,包括需要的一些前置知识都说得很清楚,同时还提供了一些学习资源,强烈推荐分布式系统的初学者做一下这个lab,传送门:https://github.com/tidb-incubator/vldbss-2021/tree/master/lab0
Map-Reduce简介
我之前对Map-Reduce只是简单听说过,没有仔细了解,我首先看了lab里推荐的 MIT 6.824 课程,不过一节课1个多小时,感觉节奏稍微有点慢,我就直接找的原始论文《MapReduce: simplified data processing on large clusters》 对Map-Reduce进行了一个简要地学习。
简单来说,Map-Reduce是一个分布式的用来处理大规模数据的High-Level框架,之所以称之为High-Level是因为在设计它时就是为了让它能够处理大部分分布式数据处理任务,即适用范围要广,所以它的抽象程度会比较高。
整个Map-Reduce框架的主要组成部分如下图所示:
执行流程大致为:
- 读入输入文件
- Master分配分布式集群中的worker(一台可以用于计算的服务器)对输入数据执行map操作,并将执行结果写入该worker的本地磁盘
- map操作的结果成功写入磁盘后会通知Master,此时Master会分配一个reduce任务给一台空闲worker 同时告诉它去哪可以取到map操作的结果,这时这台worker会通过RPC(Remote Procedure Call,远程过程调用)取到map操作的结果,然后以此作为输入执行reduce操作,最后将reduce操作的结果输出为一个单独的文件,注意此时与map操作结果直接写入worker本地不同,reduce操作结果的输出目的地一般是GFS等分布式文件系统或者是作为下一轮Map-Reduce的输入
在这整个过程中,只有Map操作和Reduce操作是由用户自己编写,其余的均由框架完成。
也就是说,我们只关心Map函数和Reduce函数应该完成哪些任务,就可以完成各种各样的分布式数据处理任务,使得分布式处理任务的编码成本得以降低,这也是Map-Reduce框架的主要意义所在。
Lab0 任务一:完成 Map-Reduce 框架
这次lab设计得对初学者十分友好,lab中提供的源码对Map-Reduce框架已经完成了很大一部分,包括Map阶段的代码都是完整的,所以初学者经过简单地学习go语言之后,可以通过阅读框架中已经写好的代码进行快速地上手。
从最简单的的开始,我们首先对Reduce阶段进行实现。
Reduce阶段需要拿到对应的map操作的执行结果,然后以此作为输入执行reduce操作,最后将执行结果输出为一个文件即可,具体代码如下:
// YOUR CODE HERE :)
// hint: don't encode results returned by ReduceF, and just output
// them into the destination file directly so that users can get
// results formatted as what they want.
if t.phase != reducePhase {
panic("unknown task phase")
}
reduceRes := make(map[string][]string)
for i := 0; i < t.nMap; i++ {
//通过文件名规则拿到对应map的结果
mappedFileName := reduceName(t.dataDir, t.jobName, i, t.taskNumber)
mappedFile, err := os.Open(mappedFileName)
if err != nil {
log.Fatalln("open mapped file error: ", mappedFileName, " error: ", err)
}
dec := json.NewDecoder(mappedFile)
for {
var kv KeyValue
if err := dec.Decode(&kv); err != nil {
break //EOF
}
if _, exist := reduceRes[kv.Key]; !exist {
reduceRes[kv.Key] = make([]string, 0)
}
reduceRes[kv.Key] = append(reduceRes[kv.Key], kv.Value)
}
SafeClose(mappedFile, nil)
}
fs, bs := CreateFileAndBuf(mergeName(t.dataDir, t.jobName, t.taskNumber))
for k, v := range reduceRes {
//输出Reduce结果
WriteToBuf(bs, t.reduceF(k, v))
}
SafeClose(fs, bs)
观察发现,Map-Reduce 框架需要补充的部分就还剩:Master分发的Map任务都执行完毕之后的执行逻辑
分析得知,map任务都执行完毕之后需要接着分发Reduce任务,对着上面已经写好的Map任务分发逻辑,可以照猫画虎地对R