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

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 支持多种压缩格式,不同格式在压缩率、速度和可切分性上各有优劣,需根据场景选择。

压缩格式核心特性对比

压缩格式 Hadoop 自带 算法 文件扩展名 可切分性 压缩率 压缩速度 解压缩速度 适用场景
DEFLATE 是 DEFLATE .deflate 否 中 中 中 通用中间数据压缩
Gzip 是 DEFLATE .gz 否 高 中 中 结果数据存储(压缩率优先)
Bzip2 是 Bzip2 .bz2 是 最高 慢 中 冷数据归档(压缩率优先,不计较速度)
LZO 否(需安装) LZO .lzo 是(需建索引) 中 快 快 大文件中间数据(需切分场景)
Snappy 否(需安装) Snappy .snappy 否 中低 最快 最快 实时 / 高吞吐场景(速度优先)

选型就三条:

  1. 要速度 → Snappy(Shuffle 中间数据首选)
  2. 要空间 → Bzip2 或 Gzip(结果存储、冷归档)
  3. 要大文件 + 要并行 → LZO(单文件 > 1GB,需要切分处理)

实际生产中最常用的组合:

  • Map 输出(Shuffle)→ Snappy:速度快,网络传输省 50%+
  • Reduce 输出(结果)→ Gzip:压缩率不错,Hadoop 自带,不用额外装

怎么配?三个阶段的压缩配置

阶段1:输入压缩------自动解压,啥都不用配

Hadoop 通过文件扩展名自动识别压缩格式:

  • .gz → Gzip
  • .bz2 → Bzip2
  • .lzo → LZO
  • .snappy → Snappy

只要是这些后缀,MapReduce 读的时候自动解压,不需要额外配置。

阶段2:Map 输出压缩------Shuffle 阶段,最推荐开启

xml 复制代码
<!-- mapred-site.xml -->
<property>
  <name>mapreduce.map.output.compress</name>
  <value>true</value>
</property>
<property>
  <name>mapreduce.map.output.compress.codec</name>
  <value>org.apache.hadoop.io.compress.SnappyCodec</value>
</property>

或者在代码里配置:

java 复制代码
conf.setBoolean("mapreduce.map.output.compress", true);
conf.setClass("mapreduce.map.output.compress.codec", SnappyCodec.class, CompressionCodec.class);

阶段3:Reduce 输出压缩------结果存到 HDFS,省空间

xml 复制代码
<!-- 启用 Reduce 输出压缩 -->  
<property>  
  <name>mapreduce.output.fileoutputformat.compress</name>  
  <value>true</value>  
</property>  

<!-- 指定 Reduce 输出压缩格式(推荐 Gzip 或 Bzip2) -->  
<property>  
  <name>mapreduce.output.fileoutputformat.compress.codec</name>  
  <value>org.apache.hadoop.io.compress.GzipCodec</value>  
</property>  

<!-- 压缩类型:BLOCK(块压缩,更高效)或 RECORD(记录压缩) -->  
<property>  
  <name>mapreduce.output.fileoutputformat.compress.type</name>  
  <value>BLOCK</value>  
</property>

或者在代码里配置:

java 复制代码
// 启用 Reduce 输出压缩  
FileOutputFormat.setCompressOutput(job, true);  
// 设置压缩格式  
FileOutputFormat.setOutputCompressorClass(job, GzipCodec.class);  
// 设置压缩类型(块压缩)  
job.getConfiguration().set("mapreduce.output.fileoutputformat.compress.type", "BLOCK");  

两个特殊格式的说明

可切分(Splittable)为什么重要?

默认情况下,一个文件对应一个 InputSplit,然后一个 Map 任务处理。

  • 如果文件是 不可切分 的压缩格式(Gzip、Snappy),一个 10GB 的 Gzip 文件只能由一个 Map 任务处理,完全失去并行性
  • 如果文件是 可切分 的压缩格式(Bzip2、LZO),文件可以按 Block 切开,多个 Map 并行处理

结论:大文件 > 1GB,用 Bzip2 或 LZO(带索引),否则单个 Map 任务会卡死。

LZO 的"索引"是什么?

LZO 本身不可切分,但可以通过建索引实现切分:

bash 复制代码
# 给 LZO 文件建索引
hadoop jar hadoop-lzo.jar com.hadoop.compression.lzo.DistributedLzoIndexer /input/file.lzo

建完索引后,Hadoop 就能按 Block 切分 LZO 文件并行处理。

Snappy 和 LZO 需要额外安装

Hadoop 自带的压缩格式只有:DEFLATE、Gzip、Bzip2。

Snappy 和 LZO 需要额外安装。

Snappy 安装(每个节点都要装):

bash 复制代码
# 1. 下载并安装 Snappy 库  
sudo yum install snappy snappy-devel  

# 2. 重新编译 Hadoop(确保支持 Snappy)  
# 下载 Hadoop 源码,编译时添加 Snappy 支持  
mvn package -Pdist,native -DskipTests -Dtar -Dsnappy.lib=/usr/lib64  

# 3. 验证安装  
hadoop checknative | grep snappy  
# 输出 "snappy: true" 表示成功  

LZO 安装:

bash 复制代码
# 1. 安装 LZO 库  
sudo yum install lzo lzo-devel  

# 2. 安装 Hadoop-LZO 插件(需编译)  
git clone https://github.com/twitter/hadoop-lzo.git  
cd hadoop-lzo  
mvn package -DskipTests  

# 3. 部署 JAR 包到 Hadoop 库目录  
cp target/hadoop-lzo-*.jar $HADOOP_HOME/share/hadoop/common/  

# 4. 为 LZO 文件建索引(否则无法切分)  
hadoop jar $HADOOP_HOME/share/hadoop/common/hadoop-lzo-*.jar com.hadoop.compression.lzo.DistributedLzoIndexer /input/file.lzo 

检查 Hadoop 支持哪些压缩格式:

bash 复制代码
hadoop checknative

怎么判断压缩生效了?

1. 看日志

作业运行日志里会有 "Compression" 相关输出。如果配置了 Map 输出压缩,能看到:

lua 复制代码
Map output compression: true
Compression codec: org.apache.hadoop.io.compress.SnappyCodec

2. 看文件大小和扩展名

Reduce 输出文件如果配置了 Gzip 压缩,生成的文件后缀是 .gz,大小明显小于未压缩的版本。

3. 看 Shuffle 传输量

在 YARN UI 里看作业的 "Shuffle Bytes"------如果压缩生效,这个数值会显著减少。

几个常见问题

1. 压缩反而更慢了?

可能是两个原因:

  • CPU 已经是瓶颈(加压缩只会更慢)
  • 用了 Bzip2 这种高压缩率的格式(算法本身就慢)

解决: 换 Snappy 或 LZO,把"高压缩率"换成"高速度"。

2. Map 输出压缩用什么格式?

无脑 Snappy。 速度快、CPU 开销小,Shuffle 网络传输省 50%+。

3. 结果存储用什么格式?

  • 长期存储(归档)→ Bzip2(压得最小)
  • 日常存储 → Gzip(压缩率好,Hadoop 自带)
  • 后续还要做 MapReduce 处理且文件很大 → LZO(可切分)

4. Gzip 文件太大,只有一个 Map 处理怎么解决?

没有办法。Gzip 不可切分。要么换格式(LZO/Bzip2),要么在数据源头就切成多个 Gzip 文件。

相关推荐
行百里er1 小时前
Redis 核心数据结构(二)——List 与消息队列
redis·后端
知守观1 小时前
AI 代码审查实战:2022年Java老项目挑出20个坑,老炮只认15个
后端
创新技术阁1 小时前
FastapiAdmin插件介绍
前端·后端·fastapi
用户051610461671 小时前
1688 / 京东 / 淘宝 item_get 返回字段逐个拆:三个平台的真实报文差在哪
后端
行百里er1 小时前
Redis 核心数据结构(三)——Hash,把一堆字段塞进一个 Key
redis·后端
能掘金的小能手1 小时前
「数据库连不上」——一次误判,和它暴露出的排查盲区
后端
用户EasyAdminBlazor1 小时前
Blazor Admin 关联表怎么处理?EasyAdminBlazor Navigate、Include、Join 实战
后端
一粒麦仔1 小时前
llama.cpp / Ollama / LM Studio:本地 LLM 推理栈的硬核拆解
人工智能·后端·架构
一勺思维1 小时前
做完半年 AI Agent 应用,我踩过的 5 个坑,全是"看起来解决了"的那种
后端