Skip to content

Daily Study

更新: 2/9/2026 字数: 0 字 时长: 0 分钟

Daily Plan

#todo

  • [ ]

Flink学习

涉及到的内容:clickhouse flink elasticsearch hadoop hdfs

mapreduce回顾

设计目标是为了处理PB级海量数据:

  1. 内存存不下:如果用 HashMap,你需要把所有 Key 存入内存,数据量太大直接 OOM。
  2. 归并的需求:通过排序,相同的 Key 会自动靠在一起(比如 Apple, Apple, Apple, Banana, Banana...)。这样 Reducer 只需要顺序读取,读到一个新 Key 就可以处理上一组数据,完全不需要把所有数据加载到内存。

基于磁盘执行任务:Map 跑完 -> 写磁盘 -> Shuffle -> Reduce 跑完 -> 写磁盘

工作流程

  1. Input Split (切片)

    • 系统将 HDFS 上的大文件切割成物理上的 Block(默认 128MB)。
    • 每一个 Block 启动一个 Map Task(进程/线程)。
  2. Map Phase (映射阶段)

    • 任务:解析输入数据,处理成 <Key, Value> 对。

    • 例子:读一行文本 "Hello World Hello",输出:

      • <Hello, 1>

      • <World, 1>

      • <Hello, 1>

    • 特点:Map 任务之间完全并行,互不干扰(Shared Nothing),速度极快。

  3. Shuffle Phase

    • 目标:把 Map 输出的无序数据,整理成 Reduce 需要的有序数据。

    • 过程:

      1. Partition (分区):决定这条数据发给哪个 Reduce 节点(通常是 Hash(Key) % ReduceNum)。

      2. Sort (排序):在 Map 端内存中对 Key 进行排序。

      3. Spill (溢写):内存满了写到磁盘(Disk I/O)。

      4. Merge (合并):Reduce 端从多个 Map 节点拉取数据(Network I/O),并进行归并排序。

    • 代价:涉及大量的网络传输和磁盘读写。

  4. Reduce Phase (归约阶段)

    • 任务:接收 Shuffle 过来的数据,通常是一组 <Key, List<Value>>

    • 例子:

      • 输入:<Hello, [1, 1, 1, ...]>

      • 逻辑:sum(list)

      • 输出:<Hello, 10086>

  5. 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 排序:

  1. 针对 Key 进行排序。
  2. 目的:为了让 Reducer 可以通过线性扫描的方式处理数据,避免内存溢出,实现分组功能。
  3. 算法:内存中用 QuickSort,磁盘合并用 MergeSort

其中多路归并排序实现:使用最小堆。

  • 初始化:将 $k$ 个数组的第一个元素放入最小堆。

  • 弹出:从堆顶弹出最小值(这就是当前全局最小),写入结果集。

  • 补充:看刚才弹出的那个元素来自哪个数组(比如第 $i$ 个数组),就从第 $i$ 个数组里取下一个元素放入堆中。

  • 循环:重复步骤 2-3,直到堆为空。

菜就多练

本站访客数 人次 本站总访问量