5.3 文件格式

5.3 文件格式

  有很多文件格式自身也是数据结构。在Hive那章中,我们介绍了ORC文件——优化记录列式文件存储。Hadoop还支持其他一些流行的文件格式。

5.3.1 Sequence文件格式

  Sequence文件是包含二进制键值对的一种文件格式。Sequence文件中的每一条记录都含有一个键和键对应的值。Sequence文件把多个较小的文件合并成单个较大的文件,这样可以缓解Hadoop中由于小文件过多而产生的问题。此时,文件名作为键,键的值就是文件内容。Sequence文件因为可以切分成可配置的数据块,所以得到广泛的应用。Sequence文件还能和快速压缩方法集成,如LZO或Snappy,从而在提高处理速度的同时,还能减少存储和带宽的消耗。

  下图展示了Sequence文件内部格式。文件的开头是一个魔数(magic number),即SEQ的二进制表示,之后是1个版本字节和头部信息(header)。头部信息存有文件的元数据,如键和值的类名(字符串形式的名字)。比如键或值属于Text类,那么org.apache.hadoop.io.Text就会写入头部。类名后面是布尔值,表示是否启用了压缩,而块压缩也是按此顺序启用。之后会写入压缩编解码器的类名,接着是用户相关元数据的键值对,最后头部信息会以同步(sync)标记结尾。同步标记也能用来标记文件中记录的边界,而且它们都是随机生成的。因为同步标记会有存储开销,所以它们的总大小不会超过总文件大小的1%。这也就意味着,同步标记会出现在一组记录的后面。
Sequence文件图解

  每条记录都包含对应的元数据,如记录长度和键长度。键和值的实际内容以字节的形式跟在元数据之后。长度用IntWritable类序列化为4字节的整型值。如果启用了记录压缩,会使用头部信息中定义的压缩编解码器来压缩值,但这不会改变记录结构。当启用压缩时,键不会被压缩。

  进行块压缩时,记录被分组成块。块的大小的最小值由io.seqfile.compress.blocksize属性决定。在每个块的开始位置会写入一个同步标记。这个同步标记为16字节,由(UID()+’@’+时间或网络地址)表达式的hash值所组成。块压缩也会对键做压缩。块使用VIntWritable序列化来存储记录数,键长度和键。

  读写Sequence文件

  以下代码演示了如何使用SequenceFile格式来读写文件。writeSequenceFile方法获取待转换的源文件路径和输出文件路径这两个参数。SequenceFile类中的createWriter静态方法用于创建写入器处理器。写入处理器的append方法获取键和值,并把它们追加写入文件。以下代码读取一个CSV文件,然后以行数为键,行内容为值写入输出文件。

package MasteringHadoop;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.*;
import org.apache.hadoop.util.ReflectionUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;


public class MasteringHadoopSequenceFile {

    public static void writeSequenceFile(String textFile, String seqFile) throws IOException{

        Path readPath = new Path(textFile);
        Path writePath = new Path(seqFile);
        Configuration conf = new Configuration(false);
        FileSystem fs = FileSystem.get(URI.create(textFile), conf);
        BufferedReader bufferedReader = null;
        SequenceFile.Writer sequenceFileWriter = null;

        try{

            bufferedReader = new BufferedReader
                    (new InputStreamReader
                            (fs.open(readPath)));
            //创建Sequence写入处理器
            sequenceFileWriter = SequenceFile.createWriter(conf,
                                                            SequenceFile.Writer.file(writePath),
                                                            SequenceFile.Writer.keyClass(LongWritable.class),
                                                            SequenceFile.Writer.valueClass(Text.class));
            String line = null;
            LongWritable key = new LongWritable();
            Text value = new Text();
            long lineCount = 0;

            while((line = bufferedReader.readLine()) != null){
                key.set(lineCount);
                lineCount++;
                value.set(line);
                sequenceFileWriter.append(key, value);
            }
        }
        catch(IOException ioEx){
            ioEx.printStackTrace();
        }
        finally{
           if(sequenceFileWriter != null)
                sequenceFileWriter.close();

           if(bufferedReader != null)
                bufferedReader.close();
        }
   }

   public static void readSequenceFile(String seqFile) throws IOException{

       Path readPath = new Path(seqFile);
       Configuration conf = new Configuration(false);
       FileSystem fs = FileSystem.get(URI.create(seqFile), conf);

       SequenceFile.Reader reader = null;

       try{
         //创建读取器
         reader = new SequenceFile.Reader(conf, SequenceFile.Reader.file(readPath));
         //可以从Sequence文件的头部信息中获取键和值的类型。ReflectionUtils有些工具方法可以基于这些类型创建对象。
         Writable key = (Writable)ReflectionUtils.newInstance(reader.getKeyClass(), conf);
         Writable value = (Writable)ReflectionUtils.newInstance(reader.getValueClass(), conf);

         while(reader.next(key,value)){
             System.out.println("key: " + key.toString());
             //读取文件时,syncSeen方法可指出在文件中是否遇到了同步标记
             if(reader.syncSeen()){
                 System.out.println("sync: ");
             }
         }
       }
       catch(IOException ioEx){
           ioEx.printStackTrace();
       }
       finally{
           if(reader !=  null){
            reader.close();
           }
       }
   }

    public static void main(String[] args){
       try{
        writeSequenceFile(args[0], args[1]);
        readSequenceFile(args[1]);
       }
       catch(IOException ioEx){
           ioEx.printStackTrace();
       }
       System.out.println("Task has finished!");
    }
}

执行参数:

./input/countrycodes.txt ./output/seqoutput

以下Hadoop指令也可以读取Sequence文件:

hadoop fs -text /usr/input/countrycodes.seq

5.3.2 MapFile格式

  从结构上来说,MapFile和SequenceFile一样。此外,MapFile文件提供了对键的索引。MapFile的键必须是WritableComparable类型,值必须是Writable类型。而SequenceFile的键和值可通过任何序列化框架进行序列化。

  创建MapFile后,会生成两个相关文件,一个存有数据,另一个存有索引。这些文件都是SequenceFile类型。数据SequenceFile存有所有数据记录,并以键排序。索引SequenceFile存有键和其在文件中偏移量。索引文件中的键是通过采样而得的,所以其中不会存放所有的键。io.map.index.interval属性的值可以指定采样间隔。以下例子展示了countrycodes.map文件中的数据和索引文件:

hadoop fs -ls countrycodes.map/
Found 2 items
-rw-r--r-- 3 sandeepkaranth supergroup     10033 2014-06-08 14:20 countrycodes.map/data
-rw-r--r-- 3 sandeepkaranth supergroup       166 2014-06-08 14:35 countrycodes.map/index

hadoop fs -text countrycodes.map/index
127 5088

hadoop fs -text countrycodes.map/data
241 vi,Virgin Islands (USA)
242 vn,Vietnam
243 vu,Vanuatu
244 wf,Wallis and Futuna Islands
...

  MapFile格式有助于进行Map侧连接。在连接时,可以根据数据和索引文件的有序性来切分数据集,再将其传输给单个Map任务。创建MapFile格式文件的API类似于创建SequenceFile的API。以下代码演示了如何使用MapFile.fix()静态方法把SequenceFile转换为MapFile:

package MasteringHadoop;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.MapFile;
import org.apache.hadoop.io.SequenceFile;

import java.io.IOException;
import java.net.URI;

public class MasteringHadoopMapFile {

    public static void writeMapFile(String seqFile) throws IOException {

        Path readPath = new Path(seqFile);
        Path mapPath = new Path(readPath, MapFile.DATA_FILE_NAME);

        Configuration conf = new Configuration(false);
        FileSystem fs = FileSystem.get(URI.create(seqFile), conf);

        SequenceFile.Reader reader = null;

        try{
            reader = new SequenceFile.Reader(conf, SequenceFile.Reader.file(mapPath));
            Class keyClass = reader.getKeyClass();
            Class valueClass = reader.getValueClass();

            MapFile.fix(fs, readPath, keyClass, valueClass, false, conf);

        }
        catch(IOException ioEx){
            ioEx.printStackTrace();
        }
        catch(Exception ex){
            ex.printStackTrace();
        }
        finally{
            if(reader !=  null){
                reader.close();
            }
        }
    }

    public static void main(String[] args){

        try{
            writeMapFile(args[0]);

        }
        catch(IOException ioEx){
            ioEx.printStackTrace();
        }
    }
}

执行参数:

./output

将SequenceFile文件改名为data并复制到./output文件中,可以看到程序执行后,生成了index文件。

**5.3.3 其他数据结构

  Hadoop也支持其他可持久化的数据结构,它们都是MapFile的变体。以下列举了其中一些结构。

  • SetFile
  • ArrayFile
  • BloomMapFile
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值