MIT官方不建议将学习代码提交至公有仓库,所以此处不再展示任何和本节课程有关的代码。

1. 首先需要明白什么是分布式系统工程且为什么需要它?

目前互联网迅速发展,互联网使用的人数越来越多,针对于某些基础设施服务或高频率交易服务,单机往往不能很好的满足实际业务需求,为了更好的提高整体系统的响应效率和TPS,使系统硬件可以更好的进行扩容,我们一般采用多台机器共同来协作对外提供一致服务。如:DNS域名解析服务Google MapReduce以及一些大型数据库等。

2. 分布式系统所带来的一系列问题

  1. 复杂性:在多台机器协同工作时,并发部分的一些逻辑将直线上升。
  2. 失败情况处理:部分失败的情况是在这个环境中必需处理的问题。
  3. 数据一致性问题(针对于数据存储性质的服务):当我们在使用数据存储性质的服务时,多台机器之间的数据一致性问题如果无法解决将是致命问题,对于此问题有很多分布式共识算法来解决。

3. 我为什么要学习这门课程?

  1. 闲得蛋疼
  2. 针对于Raft分布式共识算法的兴趣使然
  3. 提高自己在面对系统的整体架构思考能力

4. 第一个分布式案例:Google MapReduce

MIT 6.824中,第一节课接触到的是MapReduce,我们需要阅读的论文是Google2004年释出的《MapReduce: Simplified Data Processing on Large Clusters》MapReduce是一个编程模型,主要用于大数据(大于1TB的数据)的处理,其原理就是:利用一个输入Key/Value Pair集合来产生一个输出Key/Value Pair的集合,MapReduce库的用户需要实现自己的两个函数来表达这个计算过程:MapReduce
Map函数通常会将输入数据进行按Key分割并统计出Key/Value键值对集合交给Reduce函数来进一步处理。

一个简单的词频统计的伪代码:

func mapFunc(filename, contents string) []mapreduce.KeyValue {
  // Key: Word
  // Value: Counts
  res := make([]mapreduce.KeyValue, 0)
  for _, word := range contents {
  	res = append(res, mapreduce.KeyValue{"Golang", 1})
  }
}

func reduceFunc(String key, values []int) int {
	// Key: Word
	// values: Word counts
	count := 0
	for _, _c := range values {
		count += _c
	}
	return count
}

5. Lab 01 Map Reduce Input and Output

此节的Lab主要是针对于common_map.godoMap()函数以及common_reduce.godoReduce()函数做补全,原本这里的代码是空的不可执行的。

此节Lab相对简单,根据注释,理解注释所说即可完成代码,doMap()主要是对用户输入文件的进行用户定义的Map函数进行调用,将结果按Key分类并切割生成Reduce操作所需的一系列中间文件;doReduce()主要实现的是对doMap()生成的中间文件进行读取,对读取到的Key/Value Pairs进行Reduce操作。

6. Lab 02 单工节点的词频统计

此节的Lab要求我们实现一个单工节点执行的词频统计,统计用户输入文件中各单词出现频率并将统计结果输出,此节可以帮助我们去了解学习Go Strings库、strings.FieldFuncstrconv库的一些常规使用操作。此节相对比较简单,稍微思考一下即可完成。

7. Lab 03 分布式的Map Reduce任务调度

此节的Lab相对来说比较困难,需要完成的是分布式的Map Reduce模型,基本的实现 MIT 6.824的老师们已经帮助完成,需要自己手动完成的是MasterMapReduce任务调度给Worker的部分,需要实现的代码要写在schedule.goschedule()函数中。

在本LabMIT 6.824官方页面给予了一些提示,这里我大概说一下大体的思路:

我们要将数量为NTask通过rpc提供的call()函数分配给注册到MasterWorker让其去执行,此处我们从一个名为registerChanchannel中获取到注册到MasterWorker地址,需要注意的是,在使用完Worker地址时要想办法归还到registerChan中,并且我们需要等待所有Worker执行任务完毕之后此函数才可返回给上级调用。

此节多动手及思考即可完成,不要出现Bug手忙脚乱即可。

8. Lab 04 分布式的Map Reduce中的错误情况处理

上一节完成了如何在分布式的Map Reduce模型中通过MasterTask调度给Worker去执行的部分,但忽略了错误处理的情况,如果对于Worker执行错误的Task,需要将Task重新分配给新的Worker去执行直到其执行成功。

9. Lab 05 实现倒排索引

在动手实验此节之前,需要首先明白什么是倒排索引(Inverted Index)

9.1 认识倒排索引(Inverted Index)

倒排索引其实这个名字翻译并不太准确,很容易让人误解为是类似于A~ZZ~A这样的“倒排”,其实叫“反转索引”或“反过来索引(过于俗气)”更容易理解,为什么这么说呢? 我在知乎看到一个非常通俗易懂的回答,摘抄到此处:

通常倒排索引常用于文档检索,文档是由许多的单词组成的,其中每个单词可以在一个文档重复出现很多次,当然,一个单词也可以出现在不同的文档中
和倒排索引对应的有一个正排索引(Forward Index):从文档的角度来看其中的单词,表示每个文档(含有文档ID)都含有哪些单词,以及每个单词出现的次数(词频)以及相对于文档首部的偏移量(单词的出现位置)。

那么倒排索引(Inverted Index)对应的其实就是:从单词角度来看文档,标识每个单词分别在哪个文档中出现过(含有文档ID),以及在个子文档中每个单词分别出现了多少次(词频)及其位置(相对于文档首部的偏移量)

本节Lab主要是实现一个简单的倒排索引,最后输出的结果应该如下:

{单词}: {文档总数} {出现文档名1},{出现文档名2}

和之前Lab 02的思路一样,首先需要在Map函数中按Key去划分,此处的区别是以单词为Key并以出现的文档名作为Value;在Reduce函数中我们对重复的文档名(Value数组)进行去重并按格式输出即可。

10. 后记

第一节课主要是帮助学习人员建立了对于分布式系统的基本概念及认识,通过阅读MapReduce论文,理解MapReduce并建立起对于MapReduce函数工作方式的基本概念,而需要完成的Lab1也相对简单,只要按照代码注释且提前熟悉Golang语法,并不难完成。