3、HDFS小文件检测:用命令、脚本和可视化看清你的数据
做小文件治理,第一步不是动手删,而是先搞清楚——你的集群里到底有多少小文件?它们分布在哪些目录?占了多少块?
我见过不少团队,上来就写脚本批量合并,结果把大文件也误伤了。说白了,没有检测就没有治理。这一章,我们就来聊聊怎么把HDFS里的小文件“揪出来”。
3.1 用HDFS自带命令快速摸底
最直接的方式,就是用hdfs dfs -count和hdfs dfs -ls。这两个命令虽然简单,但用好了能快速定位问题目录。
3.1.1 统计目录下的文件数量和大小
# 查看某个目录的统计信息
hdfs dfs -count /user/hive/warehouse
# 输出格式:目录数 文件数 文件总大小(字节) 路径
输出示例:
| 目录数 | 文件数 | 总大小 | 路径 |
|---|---|---|---|
| 12 | 8,934 | 45,678,901,234 | /user/hive/warehouse |
你看,光看这个数字,8,934个文件,总大小45GB,平均每个文件才5MB左右。嗯,这已经低于HDFS默认块大小128MB了,属于典型的小文件场景。
我的习惯:先用
hdfs dfs -count扫一遍顶层目录,哪个目录文件数异常多,就重点排查哪个。这比直接全量扫描快得多。
3.1.2 递归列出所有文件并过滤
# 递归列出所有文件,并过滤出小于128MB的文件
hdfs dfs -ls -R /user/hive/warehouse | awk '{if($5 < 134217728) print $8, $5}' | head -20
这个命令会把每个文件的大小和路径打印出来。但说实话,生产环境目录层级深、文件多,跑起来很慢。我一般只在排查单个目录时用。
注意:
hdfs dfs -ls -R在文件数超过百万级时,会非常慢,甚至导致NameNode负载飙升。别在生产高峰期跑。
3.2 编写脚本批量扫描小文件
命令行的方式适合临时查一下,但要做定期巡检、生成报告,就得写脚本了。我个人习惯用Python,因为处理数据、生成报表都方便。
3.2.1 核心思路
- 通过
subprocess调用HDFS命令,获取文件列表 - 解析输出,提取文件路径、大小、块数
- 按阈值(比如128MB)过滤出小文件
- 按目录聚合,统计每个目录下的小文件数量和总大小
3.2.2 一个可用的扫描脚本
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
HDFS小文件扫描脚本
用法:python3 scan_small_files.py --path /user/hive --threshold 134217728
"""
import subprocess
import sys
import argparse
from collections import defaultdict
def get_file_list(hdfs_path):
"""递归获取目录下所有文件信息"""
cmd = f"hdfs dfs -ls -R {hdfs_path}"
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
files = []
for line in result.stdout.strip().split('\n'):
parts = line.split()
if len(parts) < 8:
continue
# 只处理文件(以-开头)
if line.startswith('-'):
file_size = int(parts[4])
file_path = parts[7]
files.append((file_path, file_size))
return files
def scan_small_files(hdfs_path, threshold=128*1024*1024):
"""扫描小文件并按目录聚合"""
print(f"正在扫描路径: {hdfs_path}")
print(f"小文件阈值: {threshold / 1024 / 1024} MB")
files = get_file_list(hdfs_path)
print(f"共发现 {len(files)} 个文件")
# 按父目录聚合
dir_stats = defaultdict(lambda: {'count': 0, 'total_size': 0})
small_file_count = 0
for file_path, file_size in files:
parent_dir = '/'.join(file_path.split('/')[:-1])
if file_size < threshold:
dir_stats[parent_dir]['count'] += 1
dir_stats[parent_dir]['total_size'] += file_size
small_file_count += 1
print(f"其中小文件 {small_file_count} 个,占比 {small_file_count/len(files)*100:.2f}%")
print("\n按目录统计小文件分布(Top 20):")
print(f"{'目录':<60} {'小文件数':<10} {'总大小(MB)':<15}")
print("-" * 85)
# 按小文件数排序,取前20
sorted_dirs = sorted(dir_stats.items(), key=lambda x: x[1]['count'], reverse=True)[:20]
for dir_path, stats in sorted_dirs:
size_mb = stats['total_size'] / 1024 / 1024
print(f"{dir_path[:58]:<60} {stats['count']:<10} {size_mb:<15.2f}")
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='HDFS小文件扫描工具')
parser.add_argument('--path', required=True, help='要扫描的HDFS路径')
parser.add_argument('--threshold', type=int, default=128*1024*1024, help='小文件阈值(字节)')
args = parser.parse_args()
scan_small_files(args.path, args.threshold)
避坑指南:我曾经在生产环境直接跑这个脚本,没加超时控制,结果NameNode卡了5分钟。后来我加了
timeout参数,并且只扫描最近有数据写入的目录,避免全量扫描。
3.3 可视化小文件分布
光看数字不够直观。你想想看,给老板汇报的时候,一张图比一堆数字有说服力得多。我通常用两种方式:一是生成HTML报告,二是用SVG画分布图。
3.3.1 生成目录级小文件分布图
下面这张SVG图,展示了某个Hive数仓下各目录的小文件分布情况。一眼就能看出哪个目录是“重灾区”。
3.3.2 生成HTML报告
我习惯把扫描结果输出成一个自包含的HTML文件,里面包含表格和图表。这样每次巡检后,直接打开浏览器就能看。
# 生成HTML报告的示例(Python)
def generate_html_report(dir_stats, total_files, small_files):
html = """
<!DOCTYPE html>
<html>
<head>
<title>HDFS小文件巡检报告</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #3498db; color: white; }
.highlight { background-color: #ffeaa7; }
</style>
</head>
<body>
<h2>HDFS小文件巡检报告</h2>
<p>扫描时间: 2025-01-15 10:00:00</p>
<p>总文件数: {total_files}, 小文件数: {small_files}</p>
<table>
<tr><th>目录</th><th>小文件数</th><th>总大小(MB)</th></tr>
""".format(total_files=total_files, small_files=small_files)
for dir_path, stats in sorted_dirs:
html += f"<tr><td>{dir_path}</td><td>{stats['count']}</td><td>{stats['total_size']/1024/1024:.2f}</td></tr>"
html += "</table></body></html>"
return html
3.4 检测策略总结
说了这么多,我整理了一个检测策略对比表,方便你根据场景选择。
| 检测方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
hdfs dfs -count |
快速查看目录级统计 | 速度快,对NameNode压力小 | 无法看到单个文件详情 |
hdfs dfs -ls -R |
小范围目录排查 | 能看到每个文件 | 文件多时极慢 |
| Python扫描脚本 | 定期巡检、生成报告 | 可定制、可聚合、可输出报表 | 需要开发维护 |
| 可视化报告 | 汇报、趋势分析 | 直观、易于理解 | 需要额外生成步骤 |
我的建议:日常巡检用
hdfs dfs -count就够了,每周跑一次Python脚本生成报告。如果发现某个目录小文件激增,再用ls -R去细查。分层检测,效率最高。
检测只是第一步。拿到数据后,你就能回答三个关键问题:哪些目录最严重?小文件总量有多大?增长趋势如何?有了这些,下一步的治理才有方向。