Daily Study
更新: 2/9/2026 字数: 0 字 时长: 0 分钟
Daily Plan
#todo
- [ ]
Flink学习
涉及到的内容:clickhouse flink elasticsearch hadoop hdfs
mapreduce回顾
设计目标是为了处理PB级海量数据:
- 内存存不下:如果用 HashMap,你需要把所有 Key 存入内存,数据量太大直接 OOM。
- 归并的需求:通过排序,相同的 Key 会自动靠在一起(比如 Apple, Apple, Apple, Banana, Banana...)。这样 Reducer 只需要顺序读取,读到一个新 Key 就可以处理上一组数据,完全不需要把所有数据加载到内存。
基于磁盘执行任务:Map 跑完 -> 写磁盘 -> Shuffle -> Reduce 跑完 -> 写磁盘
工作流程
Input Split (切片)
- 系统将 HDFS 上的大文件切割成物理上的 Block(默认 128MB)。
- 每一个 Block 启动一个 Map Task(进程/线程)。
Map Phase (映射阶段)
任务:解析输入数据,处理成
<Key, Value>对。例子:读一行文本 "Hello World Hello",输出:
<Hello, 1><World, 1><Hello, 1>
特点:Map 任务之间完全并行,互不干扰(Shared Nothing),速度极快。
Shuffle Phase
目标:把 Map 输出的无序数据,整理成 Reduce 需要的有序数据。
过程:
Partition (分区):决定这条数据发给哪个 Reduce 节点(通常是
Hash(Key) % ReduceNum)。Sort (排序):在 Map 端内存中对 Key 进行排序。
Spill (溢写):内存满了写到磁盘(Disk I/O)。
Merge (合并):Reduce 端从多个 Map 节点拉取数据(Network I/O),并进行归并排序。
代价:涉及大量的网络传输和磁盘读写。
Reduce Phase (归约阶段)
任务:接收 Shuffle 过来的数据,通常是一组
<Key, List<Value>>。例子:
输入:
<Hello, [1, 1, 1, ...]>逻辑:
sum(list)输出:
<Hello, 10086>
Output Phase :将结果写入 HDFS 文件系统(为了可靠性,会有 3 副本复制)。
shuffle详解
该阶段会进行针对<Key, Value> 中的 Key的排序,默认情况下按照 Key的字典序进行排序。分成了 3 次排序操作,贯穿了 Map 和 Reduce 两端。
第一阶段:Map 端内存中的排序 (QuickSort)
- 场景:Map 任务输出数据时,并不是直接写磁盘,而是先写进一个内存缓冲区(Ring Buffer / 环形缓冲区,默认 100MB)。
- 动作:当缓冲区快满时,需要把数据溢写 到磁盘成临时文件。
- 排序对象:在溢写之前,MapReduce 会在内存中对这 80MB 数据进行一次快速排序 。
- 排序规则:双重排序:
- 先按 Partition ID 排序(决定这行数据发给哪个 Reducer)。
- 再按 Key 排序(保证同一个 Partition 内的数据是有序的)。
- 结果:生成的临时小文件内部是有序的。
第二阶段:Map 端磁盘文件的合并
- 场景:一个 Map 任务可能会产生很多个 80MB 的临时小文件。
- 动作:在 Map 任务结束前,需要把这些小文件合并成一个大文件。
- 排序算法:多路归并排序 。
- 原理:因为每个小文件内部已经有序了,归并排序效率极高。
- 结果:Map 端最终输出的一个大文件,对于每个 Partition 来说,内部是严格按 Key 有序的。
第三阶段:Reduce 端的归并排序
- 场景:Reducer 启动后,会通过 HTTP 从 1000 个 Map 节点拉取属于自己的那部分数据。
- 动作:Reducer 会收到 1000 个小文件(来自不同的 Map)。
- 排序算法:再次使用 多路归并排序。
- 结果:Reducer 在执行 reduce() 方法前,拿到的输入流是全局有序的。例如:Key: A, A, A, B, B, C, D...
- 分组:调用一次 reduce("A", iterator[1, 1, 1])。处理完 "A",指针往后移,直接处理 "B"。
总结 MapReduce 的 Shuffle 排序:
- 针对 Key 进行排序。
- 目的:为了让 Reducer 可以通过线性扫描的方式处理数据,避免内存溢出,实现分组功能。
- 算法:内存中用 QuickSort,磁盘合并用 MergeSort。
其中多路归并排序实现:使用最小堆。
初始化:将 $k$ 个数组的第一个元素放入最小堆。
弹出:从堆顶弹出最小值(这就是当前全局最小),写入结果集。
补充:看刚才弹出的那个元素来自哪个数组(比如第 $i$ 个数组),就从第 $i$ 个数组里取下一个元素放入堆中。
循环:重复步骤 2-3,直到堆为空。
