并行系统 Parallelism System(一)
RPC(Remote Procedure Call):远程过程调用
q1:什么是分布式系统? What is a Distributed System?
- 一个分布式系统是由多台独立的计算机组成的系统,这些计算机通过网络互相连接,并且它们协同工作,共同完成一个任务,就像它们是单一的、统一的系统一样
q2:为什么我们需要分布式系统? Why do we need Distributed Systems?
-
可伸缩性 (Scalability):
- 服务用户量增长时,一台服务器可能无法处理所有的请求
- 分布式系统可以通过增加更多的机器来线性地提升处理能力
-
可用性/容错性 (Availability/Fault Tolerance):
- 单点故障:如果你的服务只运行在一台机器上,那台机器一旦发生故障(硬件损坏、软件崩溃、断电等),整个服务就瘫痪了
- 分布式系统可以通过在多台机器上部署相同的服务来避免单点故障。即使其中一台机器出现问题,其他的机器仍然可以继续提供服务
-
地理分布 (Geographical Distribution):
- 用户可能分布在全球各地。将数据和服务放在离用户更近的地方,可以减少网络延迟,提高用户体验
- 将网站的静态资源(图片、视频)缓存在全球各地的服务器上,用户访问时从最近的服务器获取
-
资源共享 (Resource Sharing):
- 分布式系统中,可以共享昂贵的硬件资源(如高性能计算集群)或软件资源(如数据库服务)
- 云计算平台,多个用户共享同一批服务器资源
-
透明性 (Transparency):
- 虽然系统内部很复杂,但对用户来说,它应该看起来很简单,就像使用一台普通的计算机一样
- 用户不需要关心数据存储在哪里,哪个服务器正在处理他们的请求
- 并发性 (Concurrency): 多台机器同时执行操作
- 故障独立性 (Independent Failures): 组成系统的一些部分可能会独立于其他部分失效
- 网络问题 (Network Issues): 网络可能不可靠、有延迟或分区
- 一致性 (Consistency): 多个副本的数据如何保持一致
- 异构性 (Heterogeneity): 系统中可能包含不同硬件、操作系统和编程语言的机器
MapReduce
Map:
- 用于转换数据
Reduce:
- 聚合数据
MapReduce编程模型:核心抽象
- Map函数由用户编写,是数据处理的第一步,接收一个输入键/值对(k1, v1);
- Map函数的核心作用是处理这个输入对,并生成一组中间键/值对 list(k2, v2)
- 中间键/值对是Map和Reduce阶段之间沟通的桥梁,它们将被MapReduce库收集、分组并传递给Reduce函数
Map与Reduce的协同工作机制
- Map函数将原始数据转换为中间键/值对
- MapReduce库负责按键对这些中间对进行分组和排序
- 将每个唯一的中间键及其所有关联值传递给Reduce函数进行最终聚合
eg:词频统计
场景:
- q1:现在是Google,手头有海量的网页文档,你想要统计出每个单词在所有文档中出现的总次数。
- q2:如果只有几百个文档,你可能自己写个脚本就能搞定。但现在是数以亿计的文档,总大小达到数TB甚至PB级别,一台机器根本处理不过来。
挑战:
- 数据量巨大: 单机无法存储和处理
- 并行化复杂: 如何把任务分给几千台机器?
- 故障处理: 这么多机器,总有会出故障的,怎么保证计算不中断、结果不错?
- 数据传输: 机器之间的数据交换量巨大,如何高效传输?
MapReduce为解决该问题,将复杂的分布式计算抽象为两个核心阶段:
- Map(映射);2. Reduce(归纳)
MapReduce的分而治之:Map阶段
Map阶段是数据处理的起点,它负责将原始的、大规模的输入数据分解成更小的、可独立处理的中间键值对
-
输入: MapReduce系统会将海量文档自动分割成许多小块(例如,每块64MB),并将这些小块分发给集群中的不同机器(称为“Map Worker”)
-
Map函数:
- 每个Map Worker会接收到分配给它的一篇或多篇文档
- Map函数的工作就是:读取文档内容,每遇到一个单词,就发出一个 (单词, "1") 的键值对。 这里的 "1" 表示该单词出现了一次
-
map (文档名称 key, 文档内容 value): for each word w in value: EmitIntermediate (w, "1"); // 发出 (单词, "1")
-
执行示例:
-
Map Worker 1 处理文档A:
- The" -> ("The", "1") "quick" -> ("quick", "1") "brown" -> ("brown", "1") "fox" -> ("fox", "1") "jumps" -> ("jumps", "1") "over" -> ("over", "1") "the" -> ("the", "1") "lazy" -> ("lazy", "1") "dog" -> ("dog", "1")
-
Map Worker 2 处理文档B:
-
"Fox" ->
("Fox", "1")"is" ->
("is", "1")"quick" ->
("quick", "1")"and" ->
("and", "1")"lazy" ->
("lazy", "1")
-
-
-
Map阶段的中间结果:
- 每个Map Worker都在自己的本地磁盘上生成了大量的
(单词, "1")键值对 - 这些键值对是无序的,并且同一个单词可能在不同的Map Worker上都出现了
- 每个Map Worker都在自己的本地磁盘上生成了大量的
MapReduce的“合而治之”:Shuffle & Reduce阶段
Map阶段结束后,MapReduce框架会接管,执行一个关键的Shuffle(混洗)和Sort(排序) 阶段,然后才进入Reduce阶段。
-
Shuffle(数据混洗与分组):
- MapReduce系统会收集所有Map Worker生成的中间键值对
- 它会根据单词(中间键)进行分区,确保所有相同的单词都被发送到同一个Reduce Worker
- 例如,所有关于 "quick" 的
("quick", "1")都会被发送到同一个Reduce Worker - 容错体现: 如果某个Map Worker在传输数据时失败了,Master会重新调度这个Map任务,让另一个Worker重新生成数据并发送给Reduce Worker,确保数据不丢失
-
Sort(排序):
-
当Reduce Worker接收到所有分配给它的中间键值对后,它会根据键(单词)对这些数据进行排序。这样,所有相同的单词及其对应的 "1" 就会紧挨着排列
-
eg:
-
("fox", "1")(来自文档A)("Fox", "1")(来自文档B)("lazy", "1")(来自文档A)("lazy", "1")(来自文档B)("quick", "1")(来自文档A)("quick", "1")(来自文档B)
-
-
Combiner优化:
- MapReduce还允许指定一个Combiner函数
- 函数在Map Worker本地运行,对本地的中间结果进行“预聚合”
- 例如,在Map Worker 1上,它可能会把
("the", "1")和("the", "1")先聚合成("the", "2"),这样就减少了通过网络传输的数据量
-
Reduce函数(编写的逻辑)
- 每个Reduce Worker会接收到一个单词(键)和与该单词相关联的所有 "1" 的列表
- Reduce函数的工作就是:遍历这个列表,将所有的 "1" 相加,得到该单词的总计数,然后发出最终的
(单词, 总计数)键值对
-
实际执行示例(Reduce Worker 1):
-
接收到
("fox", ["1", "1"])->total_count = 1 + 1 = 2-> 发出("fox", "2")接收到
("lazy", ["1", "1"])->total_count = 1 + 1 = 2-> 发出("lazy", "2")接收到
("quick", ["1", "1"])->total_count = 1 + 1 = 2-> 发出("quick", "2")...
-
Reduce阶段的最终结果: 每个Reduce Worker会将自己处理的最终结果写入一个独立的输出文件(通常存储在分布式文件系统,如GFS中)
-
-