一、数据完整性
用户希望储存和处理数据的时候,不会有任何损失或者损坏。Hadoop提供两种校验:校验和、运行后台程序来检测数据块
1、校验和(常用循环冗余校验CRC-32)
①写入数据节点验证
--Hdfs会对写入的所有数据计算校验和,并在读取数据时验证校验和。
--元数据节点负责在验证收到的数据后,储存数据及其校验和。在收到客户端数据或复制其他datanode的数据时执行。
-- 正在写数据的客户端将数据及其校验和发送到一系列数据节点组成的管线,管线的最后一个数据节点负责验证校验和
②读取数据节点验证
--客户端读取数据节点数据也会验证校验和,将它们与数据节点中储存的校验和进行比较。
---每个数据节点都持久化一个用于验证的校验和日志。客户端成功验证一个数据块后,会告诉这个数据节点,数据节点由此更新日志。
③恢复数据
--由于hdfs储存着每个数据块的备份,它可以通过复制完好的数据备份来修复损坏的数据块来恢复数据。
④Localfilesystem类
Hadoop的LocalFileSystem类是用来执行客户端的校验和验证。当写入一个名为filename的文件时文件系统客户端会在包含文件块校验和的同一目录内建立一个名为Filename.crc的隐藏文件。
⑤ChecksumfileSystem类
--LocalFileSystem类通过ChecksumFileSystem类来完成自己的任务FileSystemrawFs;
--FileSystemchecksummedFs=newChecksumFileSystem(rawFS);可以通过CheckFileSystem的getRawFileSystem()方法获取源文件系统。
--当检测到错误,CheckFileSystem类会调用reportCheckSumFailure()方法报告错误,然后LocalFileSystem将这个出错的文件和校验和移到名为bad_files的文件夹内,管理员可以定期检查这个文件夹。
二、数据结构
基于文件的数据结构
HDFS和MR主要针对大数据文件来设计,在小文件处理上效率低.解决方法是选择一个容器,将这些小文件包装起来,将整个文件作为一条记录,可以获取更高效率的储存和处理,避免多次打开关闭流耗费计算资源.hdfs提供了两种类型的容器SequenceFile和MapFile
Sequence file由一系列的二进制key/value组成,如果key为小文件名,value为文件内容,则可以将大批小文件合并成一个大文件。Hadoop-0.21.0版本开始中提供了SequenceFile,包括Writer,Reader和SequenceFileSorter类进行写,读和排序操作。该方案对于小文件的存取都比较自由,不限制用户和文件的多少,支持Append追加写入,支持三级文档压缩(不压缩、文件级、块级别)。其存储结构如下图所示:
SequenceFile储存
文件中每条记录是可序列化,可持久化的键值对,提供相应的读写器和排序器,写操作根据压缩的类型分为3种
在储存结构上,sequenceFile主要由一个Header后跟多条Record组成,如图
前三个字节是一个Bytes SEQ代表着版本号,同时header也包括key的名称,value class , 压缩细节,metadata,以及Sync markers。Sync markers的作用在于可以读取任意位置的数据。
在recourds中,又分为是否压缩格式。当没有被压缩时,key与value使用Serialization序列化写入SequenceFile。当选择压缩格式时,record的压缩格式与没有压缩其实不尽相同,除了value的bytes被压缩,key是不被压缩的。
当保存的记录很多时候,可以把一串记录组织到一起同一压缩成一块。在Block中,它使所有的信息进行压缩,压缩的最小大小由配置文件中,io.seqfile.compress.blocksize配置项决定。
SequenceFile写操作
通过createWrite创建SequenceFile对象,返回Write实例,指定待写入的数据流如FSDataOutputStream或FileSystem对象和Path对象。还需指定Configuration对象和键值类型(都需要能序列化)。
SequenceFile通过API来完成新记录的添加操作
fileWriter.append(key,value);
MapFile
一个MapFile可以通过SequenceFile的地址,进行分类查找的格式。使用这个格式的优点在于,首先会将SequenceFile中的地址都加载入内存,并且进行了key值排序,从而提供更快的数据查找。
与SequenceFile只生成一个文件不同,MapFile生成一个文件夹。
索引模型按128个键建立的,可以通过io.map.index.interval来修改
缺点
1.文件不支持复写操作,不能向已存在的SequenceFile(MapFile)追加存储记录
2.当write流不关闭的时候,没有办法构造read流。也就是在执行文件写操作的时候,该文件是不可读取的
排序后的SequeneceFile,并且它会额外生成一个索引文件提供按键的查找.读写mapFile与读写SequenceFile
非常类似,只需要换成MapFile.Reader和MapFile.Writer就可以了。
在命令行显示mapFile的文件内容同样要用 -text
两者的区别:SequenceFile文件是用来存储key-value数据的,但它并不保证这些存储的key-value是有序的,
而MapFile文件则可以看做是存储有序key-value的SequenceFile文件。
MapFile文件保证key-value的有序(基于key)是通过每一次写入key-value时的检查机制,这种检查机制其实很简单,就是保证当前正要写入的key-value与上一个刚写入的key-value符合设定的顺序,
但是,这种有序是由用户来保证的,一旦写入的key-value不符合key的非递减顺序,则会直接报错而不是自动的去对输入的key-value排序
SequenceFile转换为MapFile
mapFile既然是排序和索引后的SequenceFile那么自然可以把SequenceFile转换为MapFile使用mapFile.fix()方法把一个SequenceFile文件转换成MapFile
三、数据类型
Hadoop定义了两个序列化相关接口
Writable和Comparable
WritableComparable接口相当于继承了上述两个接口的新接口
Public interfaceWritableComparable<T>
extendsWritable,Comparable<T>
Writable接口
基于DataInput与DatOutput的简单高效可序列化接口,就是org.apache.hadoop.io.Writable接口
几乎所有的hadoop可序列化对象都必须实现这个接口有2个方法
Write,readFiles
以IntWritable为例,它把java的int类型封装成了Writable序列化格式
可以通过set()设置它的值
new IntWritable().set(100);
new IntWritable(100);
WritableComparable接口
类似java的Comparable接口,用于类型的比较。MR其中一个阶段叫排序,默认使用Key来排序。Hadoop提供了一个优化接口RawComparator
Public interfaceRawComparator<T> extendsComparator<T>{
Publicint compare(byte[] b1,int s1,int l1,byte[] b2,int s2,int l2);
}
可以比较b1和b2,允许执行者直接比较数据流记录,而无须先把数据流反序列化成对象,这样可以避免新建对象的开销。
Writable类
ArrayWritable
TwoDArrayWritable
MapWritable
SortedMapWritable
BooleanWritable
ByteWritable
IntWritable
VIntWritable
FloatWritable
LongWritable
VLongWritable
DoubleWritable
NullWritable
Text
BytesWritable
MD5Hash
ObjectWrtiable
GenericWritable
Text
存储的数据按照UTF-8,类似String,它提供了序列化,反序列化和字节级别比较的方法。Text类替换了UTF8类。
1.unicode编码是一个很大的集合,可以容纳100多万个符号。具体的符号对应表可以查询unicode.org它只规定了符号的二进制代码,没有规定如何存储,而utf-8就是unicode的实现还有utf16等。对于单个字符字节第一位为0,后面7位为这个符号的unicode码。因此对于英语字母,utf-8编码和ASCII码是相同的。所有\u0001~\u007f会以单字节储存。\u0080~\u07ff的unicode会以双字节储存,\u0800~\uFFFF的会以3字节存储。
2.例子Text的几个方法 一旦使用多字节编码Text和String区别就明显了
Public void testText() throwsUnsupportedEncodingException{
T=new Text(“你好刘老”);
S=”你好刘老”;
assertEquals(t.getLength(),12);
assertEquals(s.getBytes(“utf-8”).length,12);
assertEquals(s.length(),4);
assertEquals(t.find(“刘”),6);
assertEquals(s.indexOf(“刘”),3);
}
Text.find()方法返回的是字节偏移量,String.indexOf返回单个编码字符的索引位置,
String.codeprintAt()和Text.charAt类似,前者通过字节偏移量来索引
Text对字符串没有String方法丰富 大多数情况下通过toString转换成String来操作
BytesWritable
相当于二进制数据数组的包装。以字节数组{1,2,3,4}它的序列化格式是4字节表示字节数 ,每2个字节表示一个数据即“0000 0004 0102 0304” 和Text一样BytesWritable也是可变的 ,可以通过set来修改
NullWritable
是writable类型的特殊类型,序列化长度为0,它充当占位符但不真在数据流中读写。NullWritable是单实例类型,通过NullWriable.get()方法获取
四、压缩减少储存文件所需空间,还可以降低其在网络上传输的时间。
压缩算法对比
算法 原始文件大小 压缩后文件大小 压缩速度 解压缩速度
Gzip 8.3G 1.8G 17.5MB/s 58MB/s
Bzip2 1.1 2.4MB/s 9.5MB/s
LZO-bset 2 4MB/s 60.6MB/s
LZO 2.9 49.3MB/s 74.6MB/s
Bzip2支持切分splitting.hdfs上文件1GB,如按照默认块64MB,那么这个文件被分为16个块。如果把这个块放入MR任务 ,将有16个map任务输入。如果算法不支持切分,后果是MR把这个文件作为一个Map输入。这样任务减少了,降低了数据的本地性。
1.CodeC
实现了一种压缩解压算法。Hadoop中压缩解压类实现CompressionCodec接口
createOutputStream来创建一个CompressionOutputStream,将其压缩格式写入底层的流
演示HDFS上一个1.bzip2算法压缩的文件解压,然后把解压的文件压缩成2.gz
2.本地库
Hadoop使用java开发,但是有些需求和操作并不适合java,所以引入了本地库 native。可以高效执行某些操作。如使用gzip压缩解压时,使用本地库比使用java时间要缩短大约10%,解压达到50%。在hadoop_home/lib/native下
在hadoop配置文件core-site.xml可以设置是否使用native
<property>
<name>Hadoop.native.lib
<value>true
</property>
默认是启用本地库,如果频繁使用原生库做压解压任务,可以使用codecpool,通过CodecPool的getCompressor方法获得Compressor对象,需要传入Codec。这样可以节省创建Codec对象开销 ,允许反复使用。
3.如何选择压缩格式
使用哪种压缩和具体应用有关,对于巨大,没有储存边界的文件如日志 可以考虑
1.储存不压缩的文件
2.使用支持切分的储存格式 bzip2