Hadoop序列化文件SequenceFile能够用于解决大量小文件(所谓小文件:泛指小于black大小的文件)问题,SequenceFile是Hadoop API提供的一种二进制文件支持。这样的二进制文件直接将<key,value>对序列化到文件里,一般对小文件能够使用这样的文件合并,即将文件名称作为key。文件内容作为value序列化到大文件里。

hadoop Archive也是一个高效地将小文件放入HDFS块中的文件存档文件格式,详情请看:hadoop Archive

可是SequenceFile文件不能追加写入,适用于一次性写入大量小文件的操作。

SequenceFile的压缩基于CompressType,请看源代码:

  /**
* The compression type used to compress key/value pairs in the
* {@link SequenceFile}.
* @see SequenceFile.Writer
*/
public static enum CompressionType {
/** Do not compress records. */
NONE, //不压缩
/** Compress values only, each separately. */
RECORD, //仅仅压缩values
/** Compress sequences of records together in blocks. */
BLOCK //压缩非常多记录的key/value组成块
}

SequenceFile读写演示样例:

import java.io.IOException;

import org.apache.hadoop.conf.Configuration;
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.SequenceFile.CompressionType;
import org.apache.hadoop.io.SequenceFile.Reader;
import org.apache.hadoop.io.SequenceFile.Writer;
import org.apache.hadoop.io.Text; /**
* @version 1.0
* @author Fish
*/
public class SequenceFileWriteDemo {
private static final String[] DATA = { "fish1", "fish2", "fish3", "fish4" }; public static void main(String[] args) throws IOException {
/**
* 写SequenceFile
*/
String uri = "/test/fish/seq.txt";
Configuration conf = new Configuration();
Path path = new Path(uri);
IntWritable key = new IntWritable();
Text value = new Text();
Writer writer = null;
try {
/**
* CompressionType.NONE 不压缩<br>
* CompressionType.RECORD 仅仅压缩value<br>
* CompressionType.BLOCK 压缩非常多记录的key/value组成块
*/
writer = SequenceFile.createWriter(conf, Writer.file(path), Writer.keyClass(key.getClass()),
Writer.valueClass(value.getClass()), Writer.compression(CompressionType.BLOCK)); for (int i = 0; i < 4; i++) {
value.set(DATA[i]);
key.set(i);
System.out.printf("[%s]\t%s\t%s\n", writer.getLength(), key, value);
writer.append(key, value); }
} finally {
IOUtils.closeStream(writer);
} /**
* 读SequenceFile
*/
SequenceFile.Reader reader = new SequenceFile.Reader(conf, Reader.file(path));
IntWritable key1 = new IntWritable();
Text value1 = new Text();
while (reader.next(key1, value1)) {
System.out.println(key1 + "----" + value1);
}
IOUtils.closeStream(reader);// 关闭read流 /**
* 用于排序
*/
// SequenceFile.Sorter sorter = new SequenceFile.Sorter(fs, comparator, IntWritable.class, Text.class, conf);
}
}

以上程序运行多次。并不会出现数据append的情况,每次都是又一次创建一个文件。且文件里只唯独四条数据。

究其原因。能够查看SequenceFile.Writer类的构造方法源代码:

out = fs.create(p, true, bufferSize, replication, blockSize, progress);

第二个參数为true,表示每次覆盖同名文件,假设为false会抛出异常。

这样设计的目的可能是和HDFS一次写入多次读取有关,不提倡追加现有文件,所以构造方法写死了true。

SequenceFile文件的数据组成形式:

一,Header

写入头部的源代码:

    /** Write and flush the file header. */
private void writeFileHeader()
throws IOException {
out.write(VERSION);//版本
Text.writeString(out, keyClass.getName());//key的Class
Text.writeString(out, valClass.getName());//val的Class out.writeBoolean(this.isCompressed());//是否压缩
out.writeBoolean(this.isBlockCompressed());//是否是CompressionType.BLOCK类型的压缩 if (this.isCompressed()) {
Text.writeString(out, (codec.getClass()).getName());//压缩类的名称
}
this.metadata.write(out);//写入metadata
out.write(sync); // write the sync bytes
out.flush(); // flush header
}

版本:

  private static byte[] VERSION = new byte[] {
(byte)'S', (byte)'E', (byte)'Q', VERSION_WITH_METADATA
};

同步标识符的生成方式:

    byte[] sync;                          // 16 random bytes
{
try {
MessageDigest digester = MessageDigest.getInstance("MD5");
long time = Time.now();
digester.update((new UID()+"@"+time).getBytes());
sync = digester.digest();
} catch (Exception e) {
throw new RuntimeException(e);
}
}

二,Record

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

Writer有三个实现类,分别相应CompressType的NONE。RECOR,BLOCK。以下逐一介绍一下(结合上面的图看):

1,NONE SequenceFile

Record直接存Record 的长度,KEY的长度,key值,Value的值

2, BlockCompressWriter

/** Append a key/value pair. */
@Override
@SuppressWarnings("unchecked")
public synchronized void append(Object key, Object val)
throws IOException {
if (key.getClass() != keyClass)
throw new IOException("wrong key class: "+key+" is not "+keyClass);
if (val.getClass() != valClass)
throw new IOException("wrong value class: "+val+" is not "+valClass); // Save key/value into respective buffers
int oldKeyLength = keyBuffer.getLength();
keySerializer.serialize(key);
int keyLength = keyBuffer.getLength() - oldKeyLength;
if (keyLength < 0)
throw new IOException("negative length keys not allowed: " + key);
WritableUtils.writeVInt(keyLenBuffer, keyLength);//每调一次,都会累加keyLength int oldValLength = valBuffer.getLength();
uncompressedValSerializer.serialize(val);
int valLength = valBuffer.getLength() - oldValLength;
WritableUtils.writeVInt(valLenBuffer, valLength);//每调一次,都会累加valLength
// Added another key/value pair
++noBufferedRecords; // Compress and flush?
int currentBlockSize = keyBuffer.getLength() + valBuffer.getLength();
if (currentBlockSize >= compressionBlockSize) {
//compressionBlockSize =  conf.getInt("io.seqfile.compress.blocksize", 1000000);
//超过1000000就会写一个Sync
  sync();
}

超过compressionBlockSize的大小。就会调用sync()方法,以下看看sync的源代码(和上面的图对比):

会写入和图中所画的各个数据项。

/** Compress and flush contents to dfs */
@Override
public synchronized void sync() throws IOException {
if (noBufferedRecords > 0) {
super.sync(); // No. of records
WritableUtils.writeVInt(out, noBufferedRecords); // Write 'keys' and lengths
writeBuffer(keyLenBuffer);
writeBuffer(keyBuffer); // Write 'values' and lengths
writeBuffer(valLenBuffer);
writeBuffer(valBuffer); // Flush the file-stream
out.flush(); // Reset internal states
keyLenBuffer.reset();
keyBuffer.reset();
valLenBuffer.reset();
valBuffer.reset();
noBufferedRecords = 0;
} }

2。RecordCompressWriter

/** Append a key/value pair. */
@Override
@SuppressWarnings("unchecked")
public synchronized void append(Object key, Object val)
throws IOException {
if (key.getClass() != keyClass)
throw new IOException("wrong key class: "+key.getClass().getName()
+" is not "+keyClass);
if (val.getClass() != valClass)
throw new IOException("wrong value class: "+val.getClass().getName()
+" is not "+valClass); buffer.reset(); // Append the 'key'
keySerializer.serialize(key);
int keyLength = buffer.getLength();
if (keyLength < 0)
throw new IOException("negative length keys not allowed: " + key); // Compress 'value' and append it
deflateFilter.resetState();
compressedValSerializer.serialize(val);
deflateOut.flush();
deflateFilter.finish(); // Write the record out
checkAndWriteSync(); // sync
out.writeInt(buffer.getLength()); // total record length record的长度
out.writeInt(keyLength); // key portion length key的长度
out.write(buffer.getData(), 0, buffer.getLength()); // data 数据
}

写入Sync:

synchronized void checkAndWriteSync() throws IOException {
if (sync != null &&
out.getPos() >= lastSyncPos+SYNC_INTERVAL) { // time to emit sync
sync();
}
}

SYNC_INTERVAL的定义:

  private static final int SYNC_ESCAPE = -1;      // "length" of sync entries
private static final int SYNC_HASH_SIZE = 16; // number of bytes in hash
private static final int SYNC_SIZE = 4+SYNC_HASH_SIZE; // escape + hash /** The number of bytes between sync points.*/
public static final int SYNC_INTERVAL = 100*SYNC_SIZE;

每2000个byte,就会写一个Sync。

总结:

Record:存储SequenceFile通用的KV数据格式,Key和Value都是二进制变长的数据。Record表示Key和Value的byte的总和。

Sync:主要是用来扫描和恢复数据的,以至于读取数据的Reader不会迷失。

Header:存储了例如以下信息:文件标识符SEQ,key和value的格式说明。以及压缩的相关信息,metadata等信息。

metadata:包括文件头所须要的数据:文件标识、Sync标识、数据格式说明(含压缩)、文件元数据(时间、owner、权限等)、检验信息等

Hadoop之SequenceFile的更多相关文章

  1. Hadoop 写SequenceFile文件 源代码

    package com.tdxx.hadoop.sequencefile; import java.io.IOException; import org.apache.hadoop.conf.Conf ...

  2. Hadoop中SequenceFile的使用

    1.对于某些应用而言,须要特殊的数据结构来存储自己的数据. 对于基于MapReduce的数据处理.将每一个二进制数据的大对象融入自己的文件里并不能实现非常高的可扩展性,针对上述情况,Hadoop开发了 ...

  3. Hadoop基础-SequenceFile的压缩编解码器

    Hadoop基础-SequenceFile的压缩编解码器 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Hadoop压缩简介 1>.文件压缩的好处 第一:较少存储文件占用 ...

  4. hadoop基础-SequenceFile详解

    hadoop基础-SequenceFile详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.SequenceFile简介 1>.什么是SequenceFile 序列文件 ...

  5. Hadoop SequenceFile数据结构介绍及读写

    在一些应用中,我们需要一种特殊的数据结构来存储数据,并进行读取,这里就分析下为什么用SequenceFile格式文件. Hadoop SequenceFile Hadoop提供的SequenceFil ...

  6. 大数据学习笔记——Hadoop编程之SequenceFile

    SequenceFile(Hadoop序列文件)基础知识与应用 上篇编程实战系列中本人介绍了基本的使用HDFS进行文件读写的方法,这一篇将承接上篇重点整理一下SequenceFile的相关知识及应用 ...

  7. Hadoop平台常用配置及优化建议

    当发现作业运行效率不理想时,需要对作业执行进行性能监测,以及对作业本身.集群平台进行优化.优化后的集群可能最大化利用硬件资源,从而提高作业的执行效率.本文记录了在hadoop集群平台搭建以及作业运行过 ...

  8. [大牛翻译系列]Hadoop(17)MapReduce 文件处理:小文件

    5.1 小文件 大数据这个概念似乎意味着处理GB级乃至更大的文件.实际上大数据可以是大量的小文件.比如说,日志文件通常增长到MB级时就会存档.这一节中将介绍在HDFS中有效地处理小文件的技术. 技术2 ...

  9. MapReduce中使用SequenceFile的方式上传文件到集群中

    如果有很多的小文件,上传到HDFS集群,每个文件都会对应一个block块,一个block块的大小默认是128M,对于很多的小文件来说占用了非常多的block数量,就会影响到内存的消耗, MapRedu ...

随机推荐

  1. jQuery添加删除节点例子第十节"员工增删表"

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/ ...

  2. 初识Redux-Saga

    Redus-saga是一个redux的中间件,主要用来简便而优雅的处理redux应用里的副作用(side effect相对于pure function这类概念而言的).它之所以可以做到这一点主要是使用 ...

  3. [转载] Kafka+Storm+HDFS整合实践

    转载自http://www.tuicool.com/articles/NzyqAn 在基于Hadoop平台的很多应用场景中,我们需要对数据进行离线和实时分析,离线分析可以很容易地借助于Hive来实现统 ...

  4. pt-query-digest

    pt-query-digest默认查询时间分布 # Query_time distribution # 1us # 10us ##################################### ...

  5. Facebook-Haystack合并小文件

    1.原文 https://www.usenix.org/legacy/event/osdi10/tech/full_papers/Beaver.pdf 2.翻译版 http://www.importn ...

  6. Mysql 用法

    一转眼,一个星期过去了,来到测试班也一个星期了,今天经历了一次,这是自己这一周的总结,也算对自己这一周的一个交代. 几个比较重要的语句: 查看数据库 show databases; 创建数据库 cre ...

  7. 【Win 10 应用开发】UI Composition 札记(七):基于表达式的动画

    上一篇烂文中,老周给大伙伴们介绍过了几个比较好玩的动画.本篇咱们深化主题,说一说基于表达式的动画.这名字好理解,就是你可以用公式 / 等式来产生动画的目标值.比如,你想让某个可视化对象的高度减半,你的 ...

  8. SSM框架通过mybatis-generator自动生成代码

    一.首先eclipse配置好maven环境,并且创建好一个SSM框架的工程 二.在pom.xml中添加plugin <build> <finalName>ssm_web< ...

  9. auxblogcms1.0.6|代码审计

    这周的审计任务,两天前的任务呀~拖延症呀~ 这次审计一个博客----auxblogcms1.0.6,网上也有所记载,我下面会做个总结. axublog是一款php个人博客系统,小巧强大的PHP+MyS ...

  10. linux基础命令整理(一)

    ls 显示当前目录内容 1)ls / (显示根目录下所有的目录和文件) 2)ls -l / (以列表的形式显示根目录下所有的目录和文件) 绝对路径和相对路径 1)绝对路径,以/开头的都是绝对路径,比如 ...