MapReduce 中的数据结构和算法
✅ 一、Map 阶段使用的数据结构和算法
1. 内存缓冲区 - 环形缓冲区
数据结构: 环形缓冲区 (Circular Buffer)
算法: 环形队列操作
实现原理
// 伪代码
class MapOutputBuffer {
private byte[] kvbuffer; // 键值对缓冲区
private int[] kvmeta; // 元数据索引
private int softLimit; // 软限制阈值
private int bufmark; // 当前写入位置
private int bufindex; // 当前读取位置
}
特点
- 空间效率:连续内存分配
- 操作效率:O(1) 的插入和读取
- 内存管理:避免频繁的内存分配
2. 排序 - 快速排序 + 归并排序
数据结构: 数组
算法: 快速排序 (QuickSort) + 归并排序 (MergeSort)
排序过程
// 溢写前排序
private void sortAndSpill() {
// 快速排序:对内存中的键值对按 key 排序
QuickSort.sort(kvbuffer, kvmeta, partition, sort_0, sort_1);
// 归并排序:合并多个溢写文件
MergeSort.merge(spillFiles);
}
3. 分区 - 哈希算法
数据结构: 散列表 (Hash Table)
算法: 一致性哈希 (Consistent Hashing)
分区算法
public class HashPartitioner<K, V> implements Partitioner<K, V> {
public int getPartition(K key, V value, int numPartitions) {
// 哈希算法:key.hashCode() & Integer.MAX_VALUE) % numPartitions
return (key.hashCode() & Integer.MAX_VALUE) % numPartitions;
}
}
✅ 二、Shuffle 阶段使用的数据结构和算法
1. 溢写文件管理 - 堆 (Heap)
数据结构: 最小堆 (Min-Heap)
算法: 堆排序 + 优先队列
文件合并算法
// 合并多个已排序的溢写文件
public class Merger {
public static <K, V> void merge(List<File> spillFiles) {
// 使用最小堆维护多个文件的最小元素
PriorityQueue<SpillRecord> heap = new PriorityQueue<>();
// 每个文件的最小元素入堆
for (File file : spillFiles) {
heap.offer(new SpillRecord(file));
}
// 堆顶元素输出,对应文件下一条记录入堆
while (!heap.isEmpty()) {
SpillRecord record = heap.poll();
output.write(record);
record.next();
if (record.hasNext()) {
heap.offer(record);
}
}
}
}
2. 网络传输 - 流式处理
数据结构: 流 (Stream)
算法: 分块传输 + 校验和
传输算法
public class ShuffleClient {
public void copyFromMap(TaskAttemptID mapId) {
// 分块读取 Map 输出
while (hasMoreData) {
byte[] chunk = readChunk();
// CRC32 校验和验证
if (validateCRC(chunk)) {
writeToFile(chunk);
}
}
}
}
3. 内存管理 - LRU 缓存
数据结构: LinkedHashMap (LRU Cache)
算法: 最近最少使用算法
缓存管理
public class InMemoryMapOutput<K, V> {
// LRU 缓存管理
private LinkedHashMap<String, MapOutput> cache =
new LinkedHashMap<String, MapOutput>(16, 0.75f, true) {
protected boolean removeEldestEntry(Map.Entry<String, MapOutput> eldest) {
return size() > maxCacheSize;
}
};
}
✅ 三、Reduce 阶段使用的数据结构和算法
1. 分组 - 分组算法
数据结构: HashMap + ArrayList
算法: 分组排序 (GroupBy Sort)
分组实现
public class GroupingCollector<K, V> {
public void collect() {
// 按 key 分组
Map<K, List<V>> groupedData = new HashMap<>();
for (Map.Entry<K, V> entry : sortedInput) {
K key = entry.getKey();
V value = entry.getValue();
groupedData.computeIfAbsent(key, k -> new ArrayList<>()).add(value);
}
}
}
2. 迭代器模式
数据结构: 迭代器 (Iterator)
算法: 惰性求值 (Lazy Evaluation)
迭代器实现
public class ReduceIterator<K, V> implements Iterator<V> {
private K currentKey;
private Iterator<V> currentValueIterator;
public boolean hasNext() {
return currentValueIterator.hasNext();
}
public V next() {
// 惰性求值:只有在需要时才获取下一个值
return currentValueIterator.next();
}
}
✅ 四、系统级数据结构和算法
1. 任务调度 - 优先队列
数据结构: 优先队列 (PriorityQueue)
算法: 任务调度算法
任务队列管理
public class TaskScheduler {
// 按优先级调度任务
private PriorityQueue<Task> taskQueue =
new PriorityQueue<>(Comparator.comparing(Task::getPriority));
}
2. 心跳机制 - 状态机
数据结构: 状态机 (State Machine)
算法: 心跳检测算法
状态管理
public enum TaskState {
NEW, RUNNING, SUCCESS, FAILED, KILLED
}
public class TaskStatus {
private TaskState state = TaskState.NEW;
private long lastHeartbeat;
public boolean isAlive() {
return System.currentTimeMillis() - lastHeartbeat < HEARTBEAT_TIMEOUT;
}
}
✅ 五、性能优化相关算法
1. 推测执行 - 统计算法
数据结构: 滑动窗口
算法: 统计分析 + 异常检测
推测执行判断
public class SpeculativeTaskDetector {
private SlidingWindow<TaskProgress> progressWindow;
public boolean shouldLaunchSpeculative(Task task) {
double avgProgress = progressWindow.getAverage();
double taskProgress = task.getProgress();
// 如果任务进度远低于平均进度,启动推测任务
return taskProgress < avgProgress * 0.5;
}
}
2. 压缩算法
数据结构: 压缩字典
算法: LZO/Snappy/GZIP 压缩算法
✅ 六、算法复杂度分析
| 阶段 | 算法 | 时间复杂度 | 空间复杂度 | 说明 |
|---|---|---|---|---|
| Map 排序 | 快速排序 | O(n log n) | O(log n) | 内存排序 |
| 溢写合并 | 归并排序 | O(n log k) | O(k) | k个文件合并 |
| 网络传输 | 流式处理 | O(n) | O(1) | 分块传输 |
| Reduce 分组 | 哈希分组 | O(n) | O(n) | HashMap 操作 |
✅ 七、实际应用示例
WordCount 中的算法应用
// Map 阶段
public void map(LongWritable key, Text value, Context context) {
// 使用 HashMap 统计词频(Combiner 优化)
Map<String, Integer> wordCount = new HashMap<>();
String[] words = value.toString().split("\\s+");
for (String word : words) {
wordCount.put(word, wordCount.getOrDefault(word, 0) + 1);
}
// 输出到缓冲区,触发排序和分区
for (Map.Entry<String, Integer> entry : wordCount.entrySet()) {
context.write(new Text(entry.getKey()), new IntWritable(entry.getValue()));
}
}
📌 面试总结
MapReduce 核心数据结构:环形缓冲区、HashMap、优先队列、迭代器。关键算法:哈希分区、快速排序、归并排序、LRU 缓存、流式处理。优化策略:内存排序、溢写合并、网络压缩、推测执行。复杂度:Map 排序 O(n log n),Reduce 分组 O(n)。
核心要点:内存管理 + 排序算法 + 哈希算法 + 流式处理

4万+

被折叠的 条评论
为什么被折叠?



