压缩

Hive 压缩配置:Map 阶段压中间数据,Reduce 阶段压最终结果

阶段 压什么 目的 推荐算法
Map 输出压缩 Map 任务输出的中间数据 减少 Shuffle 阶段网络传输 Snappy(速度快)
Reduce 输出压缩 最终写入 HDFS 的结果数据 减少存储占用 Snappy / Gzip(看场景)

选压缩算法的核心原则:中间数据要快(Snappy),结果数据要省空间(Gzip)。

Map 输出压缩:让 Shuffle 跑得更快

Map 阶段的输出会通过网络传到 Reduce 节点。数据量一大,网络就成了瓶颈。

Map 输出压缩就是把 Map 的结果先压缩再传,减少网络传输量。

配置(在 Hive 会话中执行):

-- 开启中间数据压缩
SET hive.exec.compress.intermediate=true;

-- 指定压缩算法(Snappy 速度最快,推荐)
SET mapreduce.map.output.compress.codec=org.apache.hadoop.io.compress.SnappyCodec;

什么时候该开:

  • Map 输出数据量大(每个 Map 输出 > 100MB)
  • 网络带宽是瓶颈
  • 集群 CPU 有余量(压缩消耗 CPU)

什么时候不该开:

  • Map 输出数据量小(< 10MB)
  • CPU 已经是瓶颈
  • 集群 Snappy 没装(会报错)

Reduce 输出压缩:让 HDFS 存得更省

Reduce 输出的数据最终写入 HDFS,存得越久越占空间。

Reduce 输出压缩就是压缩最终结果文件,省存储。

配置:

-- 开启最终输出压缩
SET hive.exec.compress.output=true;
SET mapreduce.output.fileoutputformat.compress=true;

-- 指定压缩算法
SET mapreduce.output.fileoutputformat.compress.codec=org.apache.hadoop.io.compress.SnappyCodec;

-- 压缩类型:BLOCK 比 RECORD 压缩比更高
SET mapreduce.output.fileoutputformat.compress.type=BLOCK;

什么时候该开:

  • 结果表数据量大(> 100GB)
  • 存储空间紧张
  • 数据是历史归档,不频繁读取

什么时候不该开:

  • 结果数据量小
  • 数据需要频繁读取(解压有额外 CPU 开销)
  • 下游系统不支持读压缩文件

常用压缩算法怎么选

算法 压缩比 速度 是否可切分 用途
Snappy 不可切分 Map 输出(要速度)
Gzip 不可切分 结果存储(要空间)
LZO 可切分(需索引) 大文件要并行读
BZIP2 最高 可切分 冷数据归档
ZSTD 不可切分 新版本支持,比 Gzip 快

选型一句话:

  • Map 输出无脑 Snappy——速度快,CPU 开销小
  • Reduce 输出看场景——日常用 Snappy,归档用 Gzip,超大文件用 LZO

配置持久化:一次配好,不用每次都 set

临时 SET 命令只在当前会话生效。如果想全局生效,写到 hive-site.xml

<!-- Map 输出压缩 -->
<property>
  <name>hive.exec.compress.intermediate</name>
  <value>true</value>
</property>
<property>
  <name>mapreduce.map.output.compress.codec</name>
  <value>org.apache.hadoop.io.compress.SnappyCodec</value>
</property>

<!-- Reduce 输出压缩 -->
<property>
  <name>hive.exec.compress.output</name>
  <value>true</value>
</property>
<property>
  <name>mapreduce.output.fileoutputformat.compress</name>
  <value>true</value>
</property>
<property>
  <name>mapreduce.output.fileoutputformat.compress.codec</name>
  <value>org.apache.hadoop.io.compress.SnappyCodec</value>
</property>
<property>
  <name>mapreduce.output.fileoutputformat.compress.type</name>
  <value>BLOCK</value>
</property>

验证压缩是否生效

方法1:看任务日志

执行 SQL 后,在 YARN 任务日志里搜:

map output compression codec
output compression codec

能看到 SnappyCodec 就说明配上了。

方法2:看输出文件

-- 查表在 HDFS 上的路径
DESC FORMATTED 表名;

-- 去 HDFS 看文件大小
hdfs dfs -ls -h /user/hive/warehouse/库名.db/表名/

压缩后的文件大小明显小于未压缩的(通常只有 1/3 到 1/4)。

方法3:用 hive -e 跑一条查询,对比时间

# 不开压缩跑一次
hive -e "SELECT COUNT(*) FROM big_table;"

# 开了压缩再跑一次
hive -e "SET hive.exec.compress.intermediate=true; SELECT COUNT(*) FROM big_table;"

压缩后 Shuffle 数据量减少,通常会快一些。

常见问题

1. 开启压缩后报错 ClassNotFoundException

Snappy 或 LZO 没装全。检查集群是否支持:

hadoop checknative | grep snappy
# 如果输出 snappy: true 说明支持

2. 数据量小,压缩后反而变慢

压缩有 CPU 开销。数据量小于 100MB 的时候,压不压区别不大,甚至可能更慢。小表就别开了。

3. Gzip 压缩的文件不能切分,一个 Map 任务处理整个文件?

对,Gzip 文件不可切分。如果你的结果文件有几十 GB,MapReduce 读它的时候只有一个 Map 在干活。解决方案:用 LZO(可切分)或把数据分成多个小文件再压缩。

4. 压缩后的数据 Hive 能直接读吗?

能。Hive 读表的时候会自动根据文件格式判断是否需要解压,你不需要额外配置。