文章目录
6.824 2017 Lecture 1: 概论
6.824: 分布式系统工程
什么是分布式系统?
- 多台一起合作的计算机
- 大型数据库, 点对点文件分享,MapReduce, DNS,等
- 很多重要的基础设施都是分布式的!
为什么要分布式?
- 把现实中分开的部件连起来
- 用分隔的方法实现安全性
- 用复制数据的方法来容错
- 通过并行的CPUs/mem/disk/net资源来扩容
但是:
- 复杂度:有很多并发的组件
- 必须考虑到局部的失败
- 优化性能需要一些技巧
为什么上这门课?
- 有意思 – 难度大,并有强力的解决办法
- 被应用在现实的系统里 – 大型站点的兴起驱使了它的应用
- 这是很火的研究领域 – 进展很多 + 不少大的未解决课题
- 实际操作 – 你会通过实验课创建正式可用的系统
课程结构
http://pdos.csail.mit.edu/6.824
职员:
- Robert Morris, lecturer
- Frans Kaashoek, lecturer
- Lara Araujo, TA
- Anish Athalye, TA
- Srivatsa Bhat, TA
- Daniel Ziegler, TA
课程组成:
- 大课
- 阅读
- 二个测验
- 实验课
- 课程项目作业(可选)
大课:
大概念,论文讨论,和实验课
阅读:
- 论文,有些是经典的,有些是新的
- 论文会描述概念和重要的细节
- 很多课程都着重论文
- 所以请在上课前先阅读论文!
- 每篇论文都会有一个简短的问题需要回答,你必须把论文的问题和回答在上课那天的晚上10点前发给我们
测验
半期测验会在上课时进行,期末测验是在最后一个星期举行
实验课目标:
-
对一些重要的技巧有深入的了解
-
体验分布式编程
-
第一个实验课要在星期五之后一周内完成,并在之后每周一次,这样持续一阵子
-
实验课1: MapReduce
-
实验课2: 用 Raft来为容错机制做数据复制
-
实验课3: 可容错的键/值存储
-
实验课4: 分片的键/值存储
最后阶段是可选的课程设计项目,以2到3个一组来完成。项目可替代实验课4.你需要设想一个项目然后与我们讨论清楚。
实验课的分取决于你通过了多少个测试用例。
我们会提供测试,这样你就能知道是否做对了。
小心:测试如果有时通过,有时失败,那么我们运行的时候有可能会失败。
在实验课查错也许很消耗时间,所以尽早行动。
可以在上班时间到办公室,然后用披萨换取提问的机会。
课题
本课是关于被应用程序使用的基础设施,关于抽象表示分布式理念,把它从应用程序的实现中隐藏起来。
三个大方面的抽象:
- 存储
- 通信
- 计算
【图表:用户,应用服务器,存储服务器】
有一些主题会重复出现。
主题:实现
RPC,线程,并发控制
主题:性能
梦想:可扩展的吞吐量
N x 服务器->N x 吞吐量(并行 CPU,存储,网络), 所以只需要购买更多计算机就能处理更多的计算负荷。
当 N 的量越来越大时,扩容变得越来越难:
- 负荷无法均衡,有些组件被孤立。
- 无法并行运行的代码:初始化,交互功能。
- 来自共享资源的瓶颈,比如网络。
主题:容错
上千台服务器,复杂的网络 -> 总会有些地方会出错
我们希望把这些错误从应用程序里隐藏起来。
我们时常想要:
- 可用性 – 应用程序即使在出错时也能保持数据
- 持久性 – 当错误修复时,应用程序的数据就变得可用。
想法:复制服务器。
- 当一个服务器出错时,客户端可以继续使用其它的服务器。
主题:一致性
通用的基础设施需要定义明确的行为。
- 比如“Get(k)会取得最近Put(k,v)的值”
达成这些行为是很难的!
- "复制"服务器很难保持完全一样。
- 客户端方面可能会在多步更新的过程中出错崩溃。
- 服务器在不适当的时机崩溃,比如,在执行之后,还没来得及回复的时候。
- 网络问题可能会让功能正常的服务器看起来像是当机了;这是“把大脑拆分成多块”后的风险。
一致性和性能是敌对关系。
- 一致性需要互相通信,比如,取得最近的 Put()的值。
- “强一致”时常会导致系统处理很慢。
- 高性能经常要强制应用程序使用“弱一致”。
人们在这个方面已经尝试过很多种设计方案。
实例分析:MapReduce
让我们来看看MapReduce (MR),作为实例。
MR 是本课程主题的一个很好的例子,并且它是实验课一的主要内容。
MapReduce 概论
上下文:在 TB级别的数据上执行长达几小时以上的计算
- 比如,分析爬虫抓取来的网站网页的图结构
- 要上千台服务器级别的计算能力才能实际解决问题
- 程序通常不是分布式计算专家们开发的
- 分布式可能会让人很痛苦,比如,处理各种错误
总体目标:
让不是分布式专家级别的程序员也能轻松将数据处理分布到很多服务器,并且有适度的效率。
- 程序员定义 Map 和 Reduce 的功能
- 顺序执行的代码,经常很简单
- MR 收到海量的输入数据,在千台级别的服务器上运行,并且把分布式处理的细节隐藏起来。
MapReduce 的抽象视图
输入数据被分割为 M个文件
Input1 -> Map -> a,1 b,1 c,1
Input2 -> Map -> b,1
Input3 -> Map -> a,1 c,1
| | |
| -> Reduce -> c,2
-----> Reduce -> b,2
- 针对每一个输入文件,MR 调用 Map(),生成 k2,v2这样的“中间”数据。每一个 Map()调用都是一个“任务”。
- 使用 k2,MR 把所有中间值的 v2都收集起来,然后把他们传给 Reduce。
- 最终 Reduce()生成一个<k2,v3>的 set,并且存在 R 个输出文件里。
例子:单词计数
输入是上千个本文文件
Map(k, v)
split v into words
for each word w
emit(w, "1")
Reduce(k, v)
emit(len(v))
MapReduce隐藏了很多让人痛苦的细节:
- 在多台服务器上开始 s/w的进程
- 追踪任务的进度和完成情况
- 移动数据
- 恢复错误
MapReduce可以很轻松的扩容:
N 台计算机可以得到 Nx 的吞吐值。
- 想象 M和 R 比 N 大(比如大量的输入文件和输出键值。
- Map()可以并行地运行,因为他们不进行交互。
- Reduce 也一样。
所以只需要购买更多的计算机就能获得更高的处理能力了。
- 不需要针对每个应用程序做专门的并行性能优化。
- 计算机比程序员便宜多了!
有什么可能会限制性能?
我们关心它,因为这就是要优化的部分。
- 是 CPU?内存?硬盘?还是网络?
在2004年,论文作者关注的是“跨区域网络的带宽”的局限问题。
[图表: 服务器, 网络交换机树型图]
注意,在 Map->Reduce 的 shuffle 过程中所有的数据都通过网络传输。
- 论文中的主交换机速率:100到200 GB/秒
- 1800台计算机,所以是55 MB 每秒,每台计算机
- 这比硬盘和内存的速率要小得多。
所以他们注重的是如何尽量减少网络间的数据移动。(不过如今数据中心的网络要快得多了)
更多的细节 (论文的图一):
- Master:给worker 分配任务
- 记住中间的输入值是 M 个 Map 的任务, R 个 Reduce 任务
- 输入文件存储于 GFS 上,每个Map输入文件有三个拷贝
- 所有的计算机都同时运行 GFS 和 MR worker
- 输入文件的数量远大于worker
- Master给每一个worker点一个 Map 任务。当之前的任务结束后,又给它新的任务
- Map worker将中间键散列映射到 R个在本地硬盘的分区里
- 在所有 Map 任务完成前,不会开始 Reduce
- Master告诉 Reducer 任务从 Map worker那里拿到中间的数据分区
- Reduce worker将最终输出结果写到 GFS 上(每一个 Reduce 任务生成一个文件)
详细设计是如何减少网络速度慢的影响?
- Map 的输入是从本地磁盘的 GFS 分片上读取的,不是从网络上读取的。
- 中间数据只通过网络传输一次。
- Map worker 将数据写入本地 磁盘,而非 GFS.
- 中间数据被分成带有很多键的文件。
- 通过网络进行大规模传输更加有效。
他们是如何获得良好的负荷均衡?
这对扩展非常重要–如果 N-1个服务器都等一个服务器完成,那是很糟的。
但确实会有一些任务比其它的要更花时间。
解决办法:让任务数量远大于 worker 数量。
- Worker 完成之前的任务后,master 就把新的任务交给 worker。
- 这样的话,就不会对完成时间有太显著的影响(希望如此)。
- 这样运行得快的服务器会比慢的作更多的任务,并且在相似的时间内完成。
容错方面是怎么样呢?
- 比如,如果在 MR 工作中,其中一个服务器奔溃怎么办?
- 让编程简单化重要的一点就是隐藏错误!
- 为什么不在最开始的时候就重启整个工作进程呢?
MR 重新运行失败的 Map() 和 Reduce()任务。
MR 需要他们是纯函数:
- 他们不在调用间保持状态。
- 除了规定好的 MR 输入与输出,他们不会读取和写入其它文件。
- 任务之间不会有隐藏的通信。
因此重新执行会生成同样的结果。
与其它并行编程方法相比,对纯函数的需求是MR的一个主要局限性。
但这却是 MR能够变得简单的关键。
Worker 奔溃的恢复细节:
Map worker 奔溃
- master 发现 worker 不再对 ping 进行回复
- 奔溃worker 的中间 Map 输出丢失了,但很可能每一个 Reduce 任务都需要它!
- master 重新运行,将任务分配给其它 GFS 的输入拷贝。
- 有一些 reducer worker 有可能已经读取了出错 worker 的中间数据。 在此我们依赖函数性,确定输出的 Map()!
- 如果 reduce已经拿到了所有的中间数据,master 不需要重新执行,虽然一个 Reduce 奔溃会强制出错的 Map 重新运行。
Reduce worker 奔溃
- 已经完成的任务是没问题的 --他们存在 GFS 里,也有副本
- master 会让其它 worker 来重新开始出错误 worker未完成的任务。
Reduce worker 在写入输出文件的时候奔溃
GFS 有原子性的重命名功能,这使输出文件在完成的时候才会出现。
所以 master 在别处重新运行 Reduce 任务是安全的。
其它的错误与问题:
如果master给二个worker同样的Map()任务会怎么样?
- 可能 master 错误地认为其中一个 worker 死机了。
- 它只会把其中一个告诉 给Reduce worker.
如果master给二个worker同样的Reduce()任务会怎么样?
- 他们二个都会同时试图写入 GFS 的相同输出文件!
- GFS文件命名的原子性会防止他们混起来,只有一个完成的文件会出现。
如果一个 worker 变得非常的慢 --“掉队者” 会怎么办?
- 可能因为硬件出了奇怪的问题
- master 会重新启动第二批最近的几个任务
如果因为硬件或者软件问题,一个 worker 算出了错误的结果怎么办?
- 那太糟了!MR 是假设你的 CPU 和软件是“一出错就停“的。
如果 master 自己奔溃怎么办?
MapReduce 对于哪些应用不适用?
- 不是所有的都适用于map/shuffle/reduce模式
- 少量数据,因为额外开销很大。 比如,不能作为网站的后台。
- 读取输入不确定(Map 和 Reduce 都不能选择输入)
- 多次的shuffle,比如网页排名(可以使用多次的 MR,但效率并不是很好)
还有其它更灵活的系统允许上面的情况,但是更为复杂。
结论
MapReduce 容易构建,支持大集群,并且普及度比较高
- 缺点是不是最有效率与最灵活的
- 优点是扩展方便
- 还有优点是很容易针对它编程 – 错误处理和数据移动都是隐藏的。
这些都是实践中非常好的权衡。
在接下来的课里,我们会看到更高级的后续技术。
好好享受实验课吧!