hadoop API 學習小結(五)

對於一些應用,需要特殊的數據結構來存儲數據。比如運行基於MapReduce的進程,當存儲數據時,將每個二進制數據塊放入它自己的文件,這樣做使得後期不容易擴展。爲此,hadoop開發了一系列高級容器。

一、SequenceFile類

包爲:org.apache.hadoop.io.SequenceFile

Hadoop的SequenceFile類爲二進制鍵值對提供了一個持續化的數據結構。它提供了 WriterReader and SequenceFile.Sorter 類能獨立執行讀、寫以及排序操作。

如果想應用於日誌文件格式,需要選擇一個鍵(如LongWritable表示時間戳)和一個值(如Writable表示日誌記錄的數量)。

用SequenceFile類作爲小型文件的容器也不錯。HDFS和MapReduce是大型文件的利器,當我們把文件打包到一個SequenceFile類中,我們能夠高效對小型文件進程存儲和處理。


(1)創建一個SequenceFile類

該類提供了一種靜態方法創建SequenceFile.Writer實例。而且有幾個重載方法。hadoop推薦使用靜態構造方法如下:

public static org.apache.hadoop.io.SequenceFile.Writer createWriter(FileContext fc,      //文件上下文
                                                                    Configuration conf,   //配置信息
                                                                    Path name,            //文件path
                                                                    Class keyClass,      //鍵類
                                                                    Class valClass,      //值類
                                                                   CompressionType compressionType, //壓縮類型
                                                                    CompressionCodec codec,   //壓縮器
                                                                    Metadata metadata,   //文件頭部metadata
                                                                    EnumSet<CreateFlag> createFlag, //給出創建的語義如overwrite
                                                                    org.apache.hadoop.fs.Options.CreateOpts... opts) //可選項
                                                             throws IOException

存儲在SequenceFile類中的鍵和值不一定必須是Writable。可以被SequenceFile類序列化和反序列的任何類型都可以使用。

在SequencdFile.Writer之後,就用append()方法寫入鍵/值對。然後在結束的時候調用close()方法。(SequenceFile.Write實現了java.io.Closeable)。

下面就是一個SequenceFile類的程序例子:

public static void main(String[] args) throws IOException(

  String uri = args[0];                                               //創建FileSystem的步驟已經在學習小結一中學過,這裏就不再做介紹

  Configuration conf = new Configuration();

  FileSystem fs = FileSystem.gei(URI.create(uri), conf);

 Paht path = new Path(uri);


  IntWritable key = new IntWritable();          //創建鍵

  Text value = new Text();                           //創建值

  SequencdFile.Writer writer = null;

  try{

          writer = SequencdFile.createWriter(fs,conf,path,key.getClass(),value.getClass() );       //創建Writer

          key.set(data);

         value.set(data);

         writer.append(key,value);

       }finally{

         IOUtils.closeStream(writer);

       }

  }


(2)讀取SequenceFile類

從頭到尾讀取序列文件,需要創建一個SequenceFile.Reader實例。反覆調用next()方法之一遍歷記錄。使用哪一方法取決於所使用的序列化框架。比如Writable類型,可以爲:

public boolean next(Writable key, Writable value);    //使用鍵值作爲參數,如果讀取是一個鍵值對,返回true。如果讀取到文件末尾,返回false。

下面就是一個讀取序列文件的例子:

public static void main(String[] args){

      String uri = args[0];

      Configuration conf = new Configuration();

      FileSystem fs = FileSystem.get(URI.create(uri),conf);

      Path path = new Path(uri);


       SequenceFile.Reader reader = null;

       try{

              reader = new SequenceFile.Reader(fs,path,conf);

              Writable key = (Writable) ReflectionUtils.newInstance(reader.getKeyClass(), conf);  //使用Reflection工具類創建key的一個實例

              Writable key = (Writable) ReflectionUtils.newInstance(reader.getValueClass(), conf);

              long position = reader.getPosition();           //取得reader當前位置

              while(reader.next(key,value)){                    //不斷往下讀取

                        String syncSeen = reader.syncSeen() ? " * " : " ";    //插入同步點

                        System.out.printf(position,syncSeen,key,value);

                         position = reader.getPosition();

                          }

                }finally{

                     IOUtils.closeStream(reader);

            }

}

此程序能顯示序列文件中同步點的位置。同步點是流中的一個點,如果reader失去對位置的判斷,同步點就可用於重新同步記錄邊界,例如在查找流中任意一個位置之後。同步點由SequenceFile.Reader來記錄,當序列文件被寫入的時候,它會每隔幾個記錄就插入一個特殊的項來標記此同步點。插入的開銷非常小。上面我就使用星號*標記同步點。

有兩種方法查找序列文件中指定的位置。第一種是seek()方法,第二種是sync(long position)方法。


三、序列文件的格式

序列文件由一個頭部和一個或多個記錄組成。

序列文件有三種類型,分別爲1無壓縮類型,2有壓縮類型,3和塊壓縮類型。它們的記錄組成不同,但序列文件頭都相同。

SequenceFile 頭格式:

  • version - 3 字節SEQ,後接1字節版本號
  • keyClassName -鍵類名
  • valueClassName - 值類名
  • compression - 布爾類型,標識是否對鍵值對啓用壓縮
  • blockCompression - 布爾類型,標識是否對鍵值對啓用塊壓縮
  • compression codec - 編碼解碼器類型
  • metadata - SequenceFile的Metadata 
  • sync - 一個同步標記記錄頭結尾
記錄的內部格式:
取決於是否啓用壓縮,如果是,要麼是記錄壓縮,要麼是塊壓縮。
1無壓縮類型:如果沒有啓用壓縮(默認設置)那麼每個記錄就由它的記錄長度(字節數)、鍵的長度,鍵和值組成。長度字段爲四字節。
2有壓縮類型:記錄壓縮格式與無壓縮格式基本相同,不同的是值字節是用定義在頭部的編碼器來壓縮。注意,鍵是不壓縮的。
3塊壓縮類型:塊壓縮一次壓縮多個記錄,因此它比記錄壓縮更緊湊,而且一般優先選擇。當記錄的字節數達到最小大小,纔會添加到塊。該最小值由io.seqfile.compress.blocksize中的屬性定義。默認值是1000000字節。格式爲記錄數、鍵長度、鍵、值長度、值。



四、MapFile類

MapFile類是經過排序的帶索引的SequenceFile,可以根據鍵進行查找。MapFile可以被認爲是java.util.Map的一種持久化形式。它會無限增長,直至超過map在內存中佔有的大小。

創建MapFile類的步驟和創建SequenceFile類差不多,這裏我們就不再介紹。

(1)它有兩個靜態成員變量:

static String DATA_FILE_NAME;         //數據文件名

static String INDEX_FILE_NAME;        //索引文件名


(2)常用方法:

void rename(FileSystem fs, String oldName, String newName)   //對已存在的map目錄重命名


long fix(FileSystem fs, Path dir, Class<? extends Writable> keyClass, Class<? extends Writable> valueClass, boolean dryrun, Configuration conf)        //該方法通過重新建立索引來定位出故障的MapFile,返回MapFile中無效的實體數

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章