Hadoop小檔案的處理方式

Bigdata_java發表於2020-09-29

一、將小檔案合併成歸檔檔案

  1. Archive簡稱為HAR,是一個高效地將小檔案放入HDFS塊中的檔案存檔工具,它能夠將多個小檔案打包成一個HAR檔案,這樣在減少namenode記憶體使用的同時,仍然允許對檔案進行透明的訪問
  2. HAR是建立在HDFS基礎之上的一個檔案系統,因此所有fs shell命令對HAR檔案均可用,只不過是檔案路徑格式不一樣,HAR的訪問路徑可以是以下兩種格式:
    har://scheme-hostname:port/archivepath/fileinarchive
    har:///archivepath/fileinarchive(本節點)
    3.這種方式不足之處,比如:a. 一旦建立,Archives便不可改變。要增加或移除裡面的檔案,必須重新建立歸檔檔案
    b.要歸檔的檔名中不能有空格,否則會丟擲異常,可以將空格用其他符號替換。

二.SequenceFile

  1. SequenceFile檔案是Hadoop用來儲存二進位制形式的key-value對而設計的一種平面檔案(Flat File)。
  2. 目前,也有不少人在該檔案的基礎之上提出了一些HDFS中小檔案儲存的解決方案,他們的基本思路就是將小檔案進行合併成一個大檔案,同時對這些小檔案的位置資訊構建索引
  3. 檔案不支援複寫操作,不能向已存在的SequenceFile(MapFile)追加儲存記錄
  4. 當write流不關閉的時候,沒有辦法構造read流。也就是在執行檔案寫操作的時候,該檔案是不可讀取的

程式碼示例

程式碼如下(示例):

@Test
/**
 * SequenceFile 讀操作
 */
public void sequenceRead() throws Exception {
	final String INPUT_PATH = "hdfs://192.168.242.101:9000/big/big.seq";
	// 獲取檔案系統
	Configuration conf = new Configuration();
	conf.set("fs.defaultFS", "hdfs://192.168.242.101:9000");
	FileSystem fileSystem = FileSystem.get(new URI(INPUT_PATH), conf);
	// 準備讀取seq的流
	Path path = new Path(INPUT_PATH);
	SequenceFile.Reader reader = new SequenceFile.Reader(fileSystem, path, conf);
	// 通過seq流獲得key和value
	Writable key = (Writable) ReflectionUtils.newInstance(reader.getKeyClass(), conf);
	Writable value = (Writable) ReflectionUtils.newInstance(reader.getValueClass(), conf);
	// 迴圈從流中讀取key和value
	long position = reader.getPosition();
	while (reader.next(key, value)) {
		// 列印當前key value
		System.out.println(key + ":" + value);
		// 移動遊標指向下一個key value
		position = reader.getPosition();
	}
	// 關閉流
	IOUtils.closeStream(reader);
}
@Test
/**
 * 多個小檔案合併成大seq檔案
 * 
 * @throws Exception
 */
public void small2Big() throws Exception {
	final String INPUT_PATH = "hdfs://192.168.242.101:9000/small";
	final String OUTPUT_PATH = "hdfs://192.168.242.101:9000/big/big.seq";
	// 獲取檔案系統
	Configuration conf = new Configuration();
	conf.set("fs.defaultFS", "hdfs://192.168.242.101:9000");
	FileSystem fs = FileSystem.get(conf);
	// 通過檔案系統獲取所有要處理的檔案
	FileStatus[] files = fs.listStatus(new Path(INPUT_PATH));
	// 建立可以輸出seq檔案的輸出流
	Text key = new Text();
	Text value = new Text();
	SequenceFile.Writer writer = SequenceFile.createWriter(fs, conf, new Path(OUTPUT_PATH), key.getClass(),
			value.getClass());
	// 迴圈處理每個檔案
	for (int i = 0; i < files.length; i++) {
		// key設定為檔名
		key.set(files[i].getPath().getName());
		// 讀取檔案內容
		InputStream in = fs.open(files[i].getPath());
		byte[] buffer = new byte[(int) files[i].getLen()];
		IOUtils.readFully(in, buffer, 0, buffer.length);
		// 值設定為檔案內容
		value.set(buffer);
		// 關閉輸入流
		IOUtils.closeStream(in);
		// 將key檔名value檔案內容寫入seq流中
		writer.append(key, value);
	}
}

三、CompositeInputFormat

  1. 用於多個資料來源的join
  2. 此類可以解決多個小檔案在進行mr操作時map建立過多的問題
  3. 此類的原理在於,它本質上是一個InputFormat,在其中的getSplits方法中,將他能讀到的所有的檔案生成一個InputSplit
  4. 使用此類需要配合自定義的RecordReader,需要自己開發一個RecordReader指定如何從InputSplit中讀取資料
    也可以通過引數控制最大的InputSplit大小 – CombineTextInputFormat.setMaxInputSplitSize(job, 25610241024);

相關文章