hadoop 小文件處理方案

hadoop hdfs 被設(shè)計(jì)用作海量數(shù)據(jù)存儲(chǔ),適合存儲(chǔ)大文件,文件在hdfs中是以block的形式存儲(chǔ)的,在hadoop 1.x中,hdfs block的默認(rèn)大小為64m,而在hadoop 2.x中,block的默認(rèn)大小為128m,可以在hdfs-site.xml文件中的dfs.block.size配置項(xiàng)修改默認(rèn)的塊大小。文件由一個(gè)或多個(gè)block組成,文件的元數(shù)據(jù)信息由namenode記錄,因此如果hdfs存儲(chǔ)大量的小文件時(shí),會(huì)占用大量的block以及namenode必須耗費(fèi)大量內(nèi)存來記錄這些文件的元數(shù)據(jù),造成存儲(chǔ)空間浪費(fèi)以及影響hdfs 集群的橫向擴(kuò)展。因此以下兩種方案可以用來處理hdfs 小文件的問題:
1.sequencefile
2.hadoop archives file

SequenceFile

sequencefile 由header和一個(gè)個(gè)記錄組成,header記錄著keyclass 類型,valueclass 類型,壓縮信息以及用戶自定義的信息,記錄record存儲(chǔ)的是真正的數(shù)據(jù)并以key-value的格式進(jìn)行存儲(chǔ),sequencefile文件按壓縮可分為無壓縮格式,記錄壓縮格式和塊壓縮格式。無壓縮格式和記錄壓縮格式相似,唯一的區(qū)別是記錄壓縮格式是值壓縮,格式如下圖所示:


image.png

而塊壓縮是對(duì)record進(jìn)行壓縮,一個(gè)塊由多個(gè)record組成,當(dāng)一個(gè)record的大小達(dá)到io.seqfile.compress.blockseze 默認(rèn)1000000字節(jié)時(shí),可加入到塊中,格式如圖所示:


image.png

示例代碼

package com.zjc.spark;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.SequenceFile;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.compress.DefaultCodec;
import org.apache.hadoop.util.LineReader;

import java.io.File;
import java.io.FileInputStream;

/**
 * Created by zjc on 2018/11/14.
 */

public class sparkApplication1 {

    static Configuration configuration = null;

    static {
        configuration = new Configuration();
        configuration.set("fs.defaultFS", "hdfs://z-cluster");
        configuration.set("dfs.nameservices", "z-cluster");
        configuration.set("dfs.ha.namenodes.z-cluster", "nn1,nn2");
        configuration.set("dfs.namenode.rpc-address.z-cluster.nn1", "192.168.1.22:8120");
        configuration.set("dfs.namenode.rpc-address.z-cluster.nn2", "192.168.1.107:8120");
        configuration.set("dfs.client.failover.proxy.provider.z-cluster", "org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider");
        configuration.set("hadoop.user.name", "hadoop4.27");
    }

    public static void main6(String[] args) {
        IntWritable key = new IntWritable();
        Text value = new Text();
        SequenceFile.Writer out = null;
        try {
            FileSystem fileSystem = FileSystem.get(configuration);
            out = SequenceFile.createWriter(configuration, SequenceFile.Writer.file(new Path("/testFile")), SequenceFile.Writer.keyClass(IntWritable.class), SequenceFile.Writer.valueClass(Text.class), SequenceFile.Writer.compression(SequenceFile.CompressionType.BLOCK, new DefaultCodec()));
            for (int i = 0; i < 100; i++) {
                key.set(100 - i);
                value.set(DATA[i % DATA.length]);
                out.append(key, value);
                if (i % 20 == 0) {
                    out.sync();//每四百條記錄添加一個(gè)同步點(diǎn)
                }

            }

        } catch (Exception e) {
            System.out.println(e);
        } finally {
            IOUtils.closeStream(out);
        }
    }

 
    public static void main18(String[] args) {
        IntWritable key = new IntWritable();
        Text value = new Text();
        SequenceFile.Reader in = null;
        try {
            in = new SequenceFile.Reader(configuration, SequenceFile.Reader.file(new Path("/testFile")));
            // in.sync(2129);
            long position = in.getPosition();
            while (in.next(key, value)) {
                System.out.println("position:" + position + "  key:" + key.get() + "  value:" + value.toString());
                position = in.getPosition();
            }
        } catch (Exception e) {
            System.out.println(e);
        } finally {
            IOUtils.closeStream(in);
        }
    }


}

可通過mr將多個(gè)小文件合并成一個(gè)sequencefile文件,但是sequencefile的缺點(diǎn)是不支持追加。

Hadoop Archives File

可通過hdfs shell命令將多個(gè)小文件創(chuàng)建為歸檔文件,歸檔示例:

創(chuàng)建歸檔文件
hadoop archive -archiveName foo.har -p /user/hadoop -r 3 dir1 dir2 /user/zoo
上面的例子使用 /user/hadoop 作為創(chuàng)建歸檔的相對(duì)歸檔目錄。/user/hadoop/dir1 和 /user/hadoop/dir2 目錄將會(huì)歸檔到 /user/zoo/foo.har 里面。歸檔操作并不會(huì)刪除輸入文件。如果你想在創(chuàng)建歸檔文件之后刪除這些輸入文件,你需要自己做。在這個(gè)例子中,因?yàn)槲覀冎付?-r 3,那么副本因子為3將會(huì)被使用。

查找文件
在 hadoop 檔案中查找文件就像在文件系統(tǒng)上執(zhí)行 ls 一樣簡單。在我們歸檔完 /user/hadoop/dir1 和 /user/hadoop/dir2 目錄,如果我們想查看歸檔里面有哪些文件,你僅僅需要使用下面命令:

hdfs dfs -ls -R har:///user/zoo/foo.har/
要理解-p 參數(shù)的重要性,讓我們?cè)倏匆槐樯厦娴睦印?如果您只是在 hadoop 存檔上使用 ls(而不是lsr)

hdfs dfs -ls har:///user/zoo/foo.har
輸出如下:

har:///user/zoo/foo.har/dir1
har:///user/zoo/foo.har/dir2
您可以回憶一下使用以下命令創(chuàng)建存檔

hadoop archive -archiveName foo.har -p /user/hadoop dir1 dir2 /user/zoo
如果我們將上面命令修改為下:

hadoop archive -archiveName foo.har -p /user/ hadoop/dir1 hadoop/dir2 /user/zoo
那么在 Hadoop 歸檔上如下使用 ls 命令:

hdfs dfs -ls har:///user/zoo/foo.har
那么你會(huì)得到如下結(jié)果:

har:///user/zoo/foo.har/hadoop/dir1
har:///user/zoo/foo.har/hadoop/dir2
請(qǐng)注意,已歸檔文件已相對(duì)于 /user/ 而不是/ user/hadoop 進(jìn)行歸檔。

Hadoop Archives 和 MapReduce
在 MapReduce 中使用 Hadoop Archives 就像使用默認(rèn)文件系統(tǒng)中的文件一樣簡單。 如果我們?cè)?HDFS 上的 /user/zoo/foo.har 路徑里面存儲(chǔ)了 Hadoop 歸檔文件,那么在 MapReduce 里面將它作為輸入文件可以使用 har:///user/zoo/foo.har。

Hadoop Archives 是根據(jù)索引文件對(duì)目標(biāo)文件進(jìn)行讀取,所以讀性能比正常讀取低下。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容