小菜鸟

java菜鸟号正在起航

MapTask 工作机制:从读数据到写磁盘,一个 Map 任务的完整一生

一个 Map 任务,从开始到结束,走五个阶段:

Read → Map → Collect → Spill → Combine

  • Read:把文件读进来,转成键值对
  • Map:跑你的业务逻辑
  • Collect:把结果写进内存缓冲区
  • Spill:缓冲区满了,写到磁盘
  • Combine:把磁盘上的小文件合并成大文件
flowchart TD
    A[InputSplit 数据分片] -->|Read 阶段| B[RecordReader 解析为 <K1, V1>]  
    B -->|Map 阶段| C[用户自定义 map 函数处理为 <K2, V2>]  
    C -->|Collect 阶段| D[写入内存缓冲区]  
    D -->|Spill 阶段| E[分区 排序 溢写至磁盘]  
    E -->|Combine 阶段| F[局部聚合合并溢写文件]  
    F --> G[输出最终中间结果文件]

Map 阶段:跑你的业务逻辑

map() 函数接收一个键值对,输出零个或多个键值对。

WordCount 的例子最直观:

protected void map(LongWritable key, Text value, Context context) {
    for (String word : value.toString().split(" ")) {
        context.write(new Text(word), new IntWritable(1));
    }
}
阅读全文 »

使用idea直接生成UML类图

阅读全文 »

MapReduce 数据压缩:用 CPU 换 IO,这笔账怎么算?

数据压缩是 MapReduce 性能优化的”常规武器”——减少磁盘写入、减少网络传输、节省存储空间。但压缩不是白给的,它消耗 CPU。

核心逻辑就一句话:用 CPU 换 IO。

  • 如果作业瓶颈在 IO(磁盘读写在等)→ 压缩能提速
  • 如果作业瓶颈在 CPU(CPU 已经跑满)→ 压缩会拖慢

压缩解决什么问题?

MapReduce 作业里,数据到处流动:

  • Map 输出 → 写到本地磁盘(Map 端 Shuffle)
  • Map 输出 → 传给 Reduce(网络传输)
  • Reduce 输出 → 写到 HDFS(结果存储)

这些环节都有 IO 和网络开销。压缩了,数据变小,IO 和网络就省了。代价是压缩和解压缩需要 CPU 算力。

什么场景该压?

压缩的本质是 用 CPU 时间换取 IO 效率,选择是否压缩需遵循以下原则:

  • IO 密集型作业(如日志分析、数据清洗):优先启用压缩(IO 瓶颈更突出);
  • 运算密集型作业(如复杂统计、机器学习):谨慎使用压缩(避免 CPU 成为新瓶颈);
  • 中间数据 / 结果数据:中间数据(Shuffle 阶段)和长期存储的结果数据建议压缩。

常用压缩格式对比与选型

阅读全文 »

MapReduce Shuffle 深度解析:数据从 Map 到 Reduce 的完整旅程

MapReduce 里有一句老话:“Map 和 Reduce 的代码好写,Shuffle 的调优最难。”

Shuffle 是 Map 输出到 Reduce 输入之间的所有数据处理——分区、排序、溢写、拉取、合并、分组。数据要在网络和磁盘之间来回倒腾,任何一个环节没配好,作业就跑得慢。

Shuffle 是什么?为什么它那么重要?

Shuffle 这个词翻译过来是”洗牌”,在 MapReduce 里就是 把 Map 的输出重新整理,让相同 Key 的数据聚到同一个 Reduce 去处理。

它干三件事:

  1. 分区:决定每条 Map 输出去哪个 Reduce
  2. 排序:让 Reduce 拿到的数据按 Key 有序
  3. 传输:把数据从 Map 节点搬到 Reduce 节点

整个 MapReduce 作业的时间,Shuffle 经常占 30%-50%。 所以 Shuffle 调优是 MapReduce 性能优化的核心。

Map 端 Shuffle:写数据、排序、溢写、合并

Map 任务一边生产数据,一边把数据准备好交给 Reduce。

第 1 步:写内存缓冲区

Map 输出的数据先写到内存缓冲区,默认 100MB。数据在这里待着,攒够了一批发到磁盘。

第 2 步:分区

每条 Map 输出按 Key 计算去哪个 Reduce。默认用 Key 的哈希值取模:

reduce分区 = hash(key) % reduce数量

如果默认分区不均匀(比如某个 Key 特别多),可以自己写 Partitioner 定制分区逻辑。

  • 默认分区器**:HashPartitioner,通过 Key 的哈希值取模分区:

    public int getPartition(K key, V value, int numReduceTasks) {  
        return (key.hashCode() & Integer.MAX_VALUE) % numReduceTasks;  
    }
  • 自定义分区:当默认分区规则不满足需求(如按业务字段分区)时,可通过继承Partitioner重写分区逻辑:

    public class CustomPartitioner extends Partitioner<Text, IntWritable> {  
        @Override  
        public int getPartition(Text key, IntWritable value, int numReduceTasks) {  
            // 按 Key 前缀分区(如 "order_001" 分到分区 0,"user_001" 分到分区 1)  
            if (key.toString().startsWith("order")) {  
                return 0 % numReduceTasks;  
            } else {  
                return 1 % numReduceTasks;  
            }  
        }  
    }

    需在 Driver 中配置:

    job.setPartitionerClass(CustomPartitioner.class);

第 3 步:排序

缓冲区里的数据按 Key 排序。每个分区内部都是有序的。

public class OrderKey implements WritableComparable<OrderKey> {  
    private String id;  
    private double price;  

    @Override  
    public int compareTo(OrderKey o) {  
        // 先按 ID 升序,再按价格降序  
        int cmp = this.id.compareTo(o.id);  
        if (cmp != 0) return cmp;  
        return Double.compare(o.price, this.price); // 价格降序  
    }  
}
阅读全文 »

Ajax 技术详解:从原生实现到 jQuery 封装及实战应用

Ajax(Asynchronous JavaScript and XML)是现代网页交互的核心技术,它通过在后台与服务器进行异步数据交换,使网页能够在不重新加载整个页面的情况下更新部分内容,显著提升了用户体验。本文将从 “原生 XMLHttpRequest 实现→JSON 数据处理→jQuery 简化方案→实战场景” 四个维度,系统讲解 Ajax 技术的原理与应用,帮你彻底掌握前后端异步通信的核心方法。

原生 Ajax:XMLHttpRequest 对象详解

原生 Ajax 基于 XMLHttpRequest(XHR)对象实现,它是浏览器提供的内置 API,用于在客户端与服务器之间建立异步通信。理解 XHR 的工作原理,是掌握 Ajax 技术的基础。

1. XHR 对象的创建

不同浏览器对 XHR 的实现略有差异(主要是早期 IE 浏览器),标准创建方式如下:

// 标准创建方法(兼容现代浏览器及 IE7+)
var xhr;
if (window.XMLHttpRequest) {
  // 现代浏览器(Chrome、Firefox、Edge 等)
  xhr = new XMLHttpRequest();
} else {
  // 兼容 IE6 及以下(使用 ActiveX 对象)
  xhr = new ActiveXObject("Microsoft.XMLHTTP");
}

注意:目前主流浏览器(包括 IE7+)均支持标准的 XMLHttpRequest,IE6 及以下已极少使用,实际开发中可简化为 var xhr = new XMLHttpRequest();

2. XHR 对象的核心属性与方法

XHR 对象通过一系列方法发送请求,并通过属性和事件处理服务器响应,核心成员如下:

阅读全文 »
0%