1 问题陈述
小文件是指文件 size 小于 HDFS 上 block 大小的文件. 这样的文件会给 hadoop 的扩展性和性能带来严重问题. 首先, 在 HDFS 中, 任何 block, 文件或者目录在内存中均以对象的形式存储, 每个对象约占 150byte, 如果有 10000000 个小文件, 每个文件占用一个 block, 则 namenode 大约需要 2G 空间. 如果存储 1 亿个文件, 则 namenode 需要 20G 空间. namenode 内存容量严重制约了集群的扩展.
访问大量小文件速度远远小于访问几个大文件. HDFS 最初是为流式访问大文件开发的, 如果访问大量小文件, 需要不断的从一个 datanode 跳到另一个 datanode, 严重影响性能. 最后, 处理大量小文件速度远远小于处理同等大小的大文件的速度. 每一个小文件要占用一个 slot, 而 task 启动将耗费大量时间甚至大部分时间都耗费在启动 task 和释放 task 上.
2 解决方案
Hadoop 本身也提供了几个解决方案, 分别为: Hadoop Archive,Sequence file 和 CombineFileInputFormat.
2.1 Hadoop Archive
Hadoop Archive 或者 HAR, 是一个高效地将小文件放入 HDFS 块中的文件存档工具, 它能够将多个小文件打包成一个 HAR 文件, 这样在减少 namenode 内存使用的同时, 仍然允许对文件进行透明的访问.
(1) 对某个目录 / foo/bar 下的所有小文件存档成 / outputdir/ zoo.har:
hadoop archive -archiveName zoo.har -p /foo/bar /outputdir
当然, 也可以指定 HAR 的大小(使用 - Dhar.block.size).
(2) HAR 的访问路径可以是以下两种格式:
- har://scheme-hostname:port/archivepath/fileinarchive
- har:///archivepath/fileinarchive(本节点)
可以这样查看 HAR 文件存档中的文件:
hadoop dfs -ls har:///user/zoo/foo.har
输出:
- har:///user/zoo/foo.har/hadoop/dir1
- har:///user/zoo/foo.har/hadoop/dir2
使用 HAR 时需要两点, 第一, 对小文件进行存档后, 原文件并不会自动被删除, 需要用户自己删除; 第二, 创建 HAR 文件的过程实际上是在运行一个 mapreduce 作业, 因而需要有一个 hadoop 集群运行此命令.
此外, HAR 还有一些缺陷: 第一, 一旦创建, Archives 便不可改变. 要增加或移除里面的文件, 必须重新创建归档文件. 第二, 要归档的文件名中不能有空格, 否则会抛出异常, 可以将空格用其他符号替换(使用 - Dhar.space.replacement.enable=true 和 - Dhar.space.replacement 参数).
2.2 SequenceFile
sequenceFile 文件是 Hadoop 用来存储二进制形式的 [Key,Value] 对而设计的一种平面文件(Flat File). 可以把 SequenceFile 当做是一个容器, 把所有的文件打包到 SequenceFile 类中可以高效的对小文件进行存储和处理.
SequenceFile 文件并不按照其存储的 Key 进行排序存储, SequenceFile 的内部类 Writer 提供了 append 功能. SequenceFile 中的 Key 和 Value 可以是任意类型 Writable 或者是自定义 Writable.
在存储结构上, SequenceFile 主要由一个 Header 后跟多条 Record 组成, Header 主要包含了 Key classname,value classname, 存储压缩算法, 用户自定义元数据等信息, 此外, 还包含了一些同步标识, 用于快速定位到记录的边界. 每条 Record 以键值对的方式进行存储, 用来表示它的字符数组可以一次解析成: 记录的长度, Key 的长度, Key 值和 value 值, 并且 Value 值的结构取决于该记录是否被压缩.
https://blog.csdn.net/bitcarmanlee/article/details/78111289
2.3 CombineFileInputFormat
我们基于 Hadoop 内置的 CombineFileInputFormat 来实现处理海量小文件, 需要做的工作就很显然了, 如下所示:
实现一个 RecordReader 来读取 CombineFileSplit 包装的文件 Block
继承自 CombineFileInputFormat 实现一个使用我们自定义的 RecordReader 的输入规格说明类
处理数据的 Mapper 实现类
配置用来处理海量小文件的 MapReduce Job
https://blog.csdn.net/flyfish778/article/details/47722551
3 Hive 的小文件处理
配置 Map 输入合并
配置 Hive 输出结果合并
在 Hive 中进行归档处理 Hive 支持将已存的分区转换为 HAR, 从而使得分区下的文件数目大大减少. 但是从 HAR 读数据需要额外的开销, 因此查询归档下数据可能会变慢.
如果不是分区表, 可以创建成外部表, 使用 har:// 协议来指定路径.
SequenceFile
控制 reducer 个数 Reducer 的数量决定了结果文件的数量. 所以在合适的情况下控制 reducer 的数量, 可以实现减少小文件数量.
引用: http://www.raincent.com/content-85-11297-1.html
4 总结
因为工作中用到这个, 总结成文, 参考多维博客, 均表明引用连接, 内容粗鄙, 勿怪!
秦凯新 于深圳
来源: https://juejin.im/post/5c1e62a6e51d455d382ea4d1