HDFS 小文件治理实战:从诊断到合并的全流程 SOP

以亿级小文件集群为原型,给出诊断定位、冷热分层治理、压缩配置与效果验证的三步走 SOP,附量化 ROI 案例与具体工具链命令

袋鼠云
袋鼠云
国产信创

HDFS 的 NameNode 将整个文件系统的命名空间和文件 Block 映射保存在内存中,每个文件(无论大小)都会占用约 150 字节的 NameNode 内存。当集群累积到亿级小文件时,NameNode 内存会膨胀到数十 GB,不仅启动缓慢,还会因 GC 压力导致整个集群响应迟钝。本文以一个从 5000 万文件缩减到 500 万文件、NameNode 内存从 16.5 GB 降至 10.5 GB 的真实治理案例为原型,给出从诊断到合并的完整 SOP。

第一步:诊断定位——找到小文件的来源和分布

治理小文件的第一步不是合并,而是搞清楚小文件从哪来、分布在哪里。

使用 HDFS fsck 扫描文件分布:

hdfs fsck / -files -blocks | grep "size < 128M" | wc -l

这个命令会列出所有小于 128 MB(默认 Block 大小)的文件。将结果按目录聚合,可以定位到产生小文件最多的表和分区。

使用 HDFS 概览页面快速摸底:

NameNode Web UI 的 "Datanodes" 页面可以看到集群总文件数和总 Block 数。如果文件数远超预期(比如单个集群超过 1 亿),基本可以确认小文件是瓶颈。

常见小文件来源:

  • Hive 动态分区写入时,每个分区值生成一个文件。日增量 1000 个分区,一天就是 1000 个文件
  • 流式写入场景中,每个微批次或每分钟写入一个小文件
  • 中间表被频繁创建和删除,产生碎片文件
  • ETL 任务的输出目录未做文件合并,每次运行都产生新文件

第二步:冷热分层治理——不同生命周期的数据用不同策略

不是所有数据都需要合并。冷数据(超过 30 天未访问)和热数据(最近 7 天内有查询)应该采用不同的治理策略。

热数据:Spark 读写合并

对于活跃查询的表,使用 Spark 重新读取并写入,控制输出文件数量:

SET hive.merge.mapredfiles = true;
SET hive.merge.size.per.task = 268435456;
SET hive.merge.smallfiles.avgsize = 134217728;

INSERT OVERWRITE TABLE hot_table PARTITION(dt)
SELECT * FROM hot_table WHERE dt >= '2026-09-01';

hive.merge.size.per.task 设为 256 MB,hive.merge.smallfiles.avgsize 设为 128 MB,确保小于 128 MB 的文件会被合并到 256 MB 左右。

冷数据:HAR 归档

对于超过 30 天未访问的历史分区,使用 Hadoop Archive(HAR)将多个小文件打包成一个归档文件:

hadoop archive -archiveName cold_202608.har \
  -p /user/hive/warehouse/cold_table/dt=20260801 \
  /user/hive/warehouse/cold_table/archived

HAR 文件在 NameNode 中只占一个文件的元数据开销,但内部保留原始目录结构,Hive 查询仍然可以直接访问。缺点是 HAR 不支持追加写入,适合只读的冷数据。

过渡数据:分区级合并

对于 7-30 天的数据,按分区粒度执行合并,避免全表重写带来不必要的 I/O 开销:

-- 按单个分区合并
INSERT OVERWRITE TABLE mid_table PARTITION(dt='20260915')
SELECT * FROM mid_table WHERE dt='20260915';

大数据基础平台 EasyMR

统一存储、计算与运维,支撑批、流、OLAP 与湖仓一体的国产化大数据底座平台

了解产品

第三步:压缩配置——在合并的同时优化存储

合并文件时同步切换存储格式和压缩算法,可以同时解决小文件问题和存储成本问题。

ORC + Snappy 是推荐组合:

SET hive.exec.compress.output = true;
SET mapreduce.output.fileoutputformat.compress.codec = org.apache.hadoop.io.compress.SnappyCodec;
SET hive.exec.orc.compress.strategy = SPEED;

CREATE TABLE merged_table STORED AS ORC
TBLPROPERTIES ("orc.compress" = "SNAPPY")
AS SELECT * FROM source_table;

ORC 格式将多个小文件的数据合并到一个列式存储文件中,Snappy 压缩在压缩比和解压速度之间取得平衡。根据实际测试,ORC Snappy 相比原始 Text 格式,存储空间通常缩减 60%-80%,查询时的 I/O 量也大幅下降。

避免使用 Gzip: Gzip 的压缩比虽然更高,但不支持 Splittable,大文件在 MapReduce/Spark 中只能由单个 Task 处理,反而会引入新的性能问题。

数据治理

统一标准、质量、安全与资产管理,沉淀高质量数据集,支撑经营决策、监管合规与 AI 应用。

了解解决方案

效果验证:量化 ROI

合并完成后,需要通过以下指标验证治理效果:

NameNode 内存: 这是最直接的指标。合并前 5000 万文件,NameNode 堆内存占用 16.5 GB;合并后 500 万文件,降至 10.5 GB。每个文件节省的 150 字节元数据在亿级规模下效果显著。

文件数量: 使用 hdfs dfs -count /path 统治目标目录的文件数。目标是将单表文件数控制在分区数 × 2 以内。

查询耗时: 对典型查询做 A/B 对比。小文件治理后,Hive 的 Map 任务数大幅减少(因为每个 Map 任务处理一个文件),任务启动和调度的开销下降,查询耗时通常缩短 30%-50%。

集群稳定性: 关注 NameNode 的 GC 次数和 GC 耗时。文件数减少后,NameNode 的内存压力降低,GC 停顿时间会明显缩短。

治理后的防反弹措施

小文件治理不是一次性工程。如果不建立防反弹机制,几周后文件数又会膨胀回去。

源头控制: 在 ETL 任务中设置 hive.merge.mapredfiles = true 和 hive.merge.smallfiles.avgsize,确保每次写入后自动合并小文件。

定期巡检: 每周运行一次文件数统计脚本,对超过阈值的表触发告警。建议阈值为:单分区文件数 > 50 或单表总文件数 > 10 万。

写入规范: 流式写入场景改用 Flink 或 Spark 的 Exactly-Once 写入模式,避免每个微批次都产生新文件。对于必须按时间切分的场景,控制切分粒度不低于 5 分钟。

小文件治理的核心思路是"诊断先行、分层治理、压缩并行、防反弹跟进"。把这四步做扎实,NameNode 的内存问题和查询性能问题可以同时得到解决。

开启Data+AI 之旅

填写表单,获取专属解决方案

400-002-1024

工作日 9:00-18:00

关注公众号

获取最新产品动态与技术干货

关注公众号

企业微信

扫码添加企业微信