个人观点:当处理海量小文件时,先将小文件进行sequenceFile操作或者类似操作处理,然后再上传到HDFS系统进行下一步的处理。(如有其它建议,欢迎留言)
一、直接上传本地栅格数据将导致的问题
根据栅格数据的特点,每层的图片个数都为上层数量的四倍。在第 20 层时,仅仅第20层的图片的数量达到了“ 2199023255552 ”张(世界地图在第一层被切成了两张图片,第二十层的数量为:4**7*2),且每张图片的大小都为 10-20kb不等。 HDFS在存储文件时,会将文件 break them into chunks,默认 inputSplit的大小与 block块的大小一致,为 128M,如果单个文件的大小小于 block块的大小则不会切分,直接将改小文件存储到一个 block块中。因此如果不对栅格数据文件做处理,将导致占据大量的 block块;由于 namenode中会存储为元数据信息,因此也将导致 namenode节点记录大量小文件的位置等元数据信息而产生压力过大,甚至 namenode节点的内存可能会被占满。
二、在本地将栅格数据处理成sequenceFile 文件
SequenceFile文件是 Hadoop 用来存储二进制形式的 key-value对而设计的一种平面文件 。通常对小文件的处理是使用 sequenceFile文件或 MapFile文件。此次选用的处理方式是使用 SequenceFile文件。( MapFile文件由两部分组成, data和 index, index作为文件的索引,存储每个 Record的 key值以及它的偏移量。 mapFile文件的检索效率较 sequenceFile文件高,但是访问 mapFile文件时需要先将索引文件加载到内存)
由于sequenceFile文件由 Key和 value组成,此处的 key值存放的为文件的路径,例如: file:/home/greatmap/World/6/109/48.jpeg; value的值存放的为图片的字节数组文件。栅格数据存放在的文件夹下,通过遍历文件夹,将所有的图片都写成一个 sequenceFile文件。
下面是具体实现过程:
public class SequenceFileTest { static String PATH = "/home/greatmap/out" ; static SequenceFile.Writer writer = null ; public static void main(String[] args) throws Exception{ Configuration conf = new Configuration(); conf.set("fs.default.name" , "file:///" ); conf.set("mapred.job.tracker" , "local" ); String path = "/home/greatmap/World/" ; URI uri = new URI(path); FileSystem fileSystem = FileSystem.get(uri, conf); writer = SequenceFile.createWriter(fileSystem, conf, new Path(PATH), Text. class , BytesWritable. class ); listFileAndWriteToSequenceFile(fileSystem,path); org.apache.hadoop.io.IOUtils.closeStream(writer); } public static void listFileAndWriteToSequenceFile(FileSystem fileSystem,String path) throws Exception{ final FileStatus[] listStatuses = fileSystem.listStatus( new Path(path)); for (FileStatus fileStatus : listStatuses) { if (fileStatus.isFile()){ Text fileText = new Text(fileStatus.getPath().toString()); System.out.println(fileText.toString()); FSDataInputStream in = fileSystem.open(new Path(fileText.toString())); byte [] buffer = IOUtils.toByteArray(in); in.read(buffer); BytesWritable value = new BytesWritable(buffer); writer.append(fileText, value); } if (fileStatus.isDirectory()){ listFileAndWriteToSequenceFile(fileSystem,fileStatus.getPath().toString()); } } }}