0%

数据压缩

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 阶段,最推荐开启

1
2
3
4
5
6
7
8
9
<!-- 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>

或者在代码里配置:

1
2
conf.setBoolean("mapreduce.map.output.compress", true);
conf.setClass("mapreduce.map.output.compress.codec", SnappyCodec.class, CompressionCodec.class);

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- 启用 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>

或者在代码里配置:

1
2
3
4
5
6
// 启用 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 本身不可切分,但可以通过建索引实现切分:

1
2
# 给 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 安装(每个节点都要装):

1
2
3
4
5
6
7
8
9
10
# 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 安装:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 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 支持哪些压缩格式:

1
hadoop checknative

怎么判断压缩生效了?

1. 看日志

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

1
2
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 文件。

欢迎关注我的其它发布渠道