分布式文件系统即是网络中多台计算机组合在一起提供一个统一存储及管理的系统。 Hadoop提供了一个文件系统接口和多个分布式文件系统实现,其中比较重要的就是HDFS(Hadoop Distributed Filesystem)了。Hadoop是一个综合性的文件系统抽象,因此它也可以集成其他文件系统的实现,如本地文件系统和Amazon S3系统及淘宝 TFS等。
1、概念模型
HDFS以流式数据访问模式来存储超大文件,运行于商业硬件集群上。
HDFS实现下来,分为两类节点,一个是namenode及secondarynode,主要目的是用于存储系统镜像及备份。
其中namenode将文件系统的元数据存储在内存中(因此该文件系统所能存储的文件总数受制于namenode的内存容量)。它维护着文件系统树及树内 的所有文件和目录,这些信息同时以两个文件(命名空间镜像文件和编辑日志文件)的形式永久保存在本地磁盘上。namenode也记录着每个文件的各个块所 在的数据节点信息(会在系统启动时有数据节点重建)。
secondarynode的作用是定期通过编辑日志文件合并命名空间镜像,以防止编辑日志过大和namenode发生故障时启用并作为新的namenode,但secondarynamenode状态总是滞后于主节点的。
另一类是datanode,其会有多个,目标就是实现具体的存储,及把文件的每个块给保存到本地磁盘上,和namenode关联起来,共同组成存储。
HDFS在存储数据时用的块的概念,默认每个块大小为64M(目的是为了最小化磁盘寻址时间)。HDFS上的文件被划分为块大小的多个分块(chunk),作为独立的存储单元,但HDFS中小于一个块大小的文件不会占据整个块的空间。因为是分布式文件系统,HDFS也提供了备份的功能,即把文件的每个块都会复制到多个机器上(默认为3个)。
2、hadoop文件系统
Hadoop有一个抽象的文件系统概念,HDFS只是其中的一个实现。抽象类org.apache.hadoop.fs.FileSystem定义了一个文件系统接口,并且该抽象类有几个具体实现。
文件系统 |
URI方案 |
Java实现 (org.apache.hadoop) |
定义 |
Local |
file |
fs.LocalFileSystem |
支持有客户端校验和本地文件系统。带有校验和的本地系统文件在fs.RawLocalFileSystem中实现。 |
HDFS |
hdfs |
hdfs.DistributionFileSystem |
Hadoop的分布式文件系统。 |
HFTP |
hftp |
hdfs.HftpFileSystem |
支持通过HTTP方式以只读的方式访问HDFS,distcp经常用在不同的HDFS集群间复制数据。 |
HSFTP |
hsftp |
hdfs.HsftpFileSystem |
支持通过HTTPS方式以只读的方式访问HDFS。 |
HAR |
har |
fs.HarFileSystem |
构建在Hadoop文件系统之上,对文件进行归档。Hadoop归档文件主要用来减少NameNode的内存使用。 |
KFS |
kfs |
fs.kfs.KosmosFileSystem |
Cloudstore(其前身是Kosmos文件系统)文件系统是类似于HDFS和Google的GFS文件系统,使用C++编写。 |
FTP |
ftp |
fs.ftp.FtpFileSystem |
由FTP服务器支持的文件系统。 |
S3(本地) |
s3n |
fs.s3native.NativeS3FileSystem |
基于Amazon S3的文件系统。 |
S3(基于块) |
s3 |
fs.s3.NativeS3FileSystem |
基于Amazon S3的文件系统,以块格式存储解决了S3的5GB文件大小的限制。 |
Hadoop对文件系统提供了许多接口,它一般使用URI方案来选取合适的文件系统实例进行交互。
3、HDFS实现
要想实现一个文件系统,除了简单的增删改查文件以及文件夹等简单的操作之外,还需要考虑的有很多,如数据的完整性、数据是否需要压缩存储,以及如何压缩、存储对象时使用的序列化及反序列化框架等很多复杂的问题。
3.1、数据的完整性
数据的完整性即指在从文件系统中读取数据时需要和当初存入到文件系统的数据保障一致,是否损坏,而不是残缺的数据。常见的错误检测门是CRC-32(循环冗余检验),任何大小的数据输入都会计算出一个32位的数据校验和。
HDFS会对写入的所有数据计算校验和,并在读取时验证校验和。它针对每个由io.bytes.per.checksum指定的数据计算校验和。默认情况下为512个字节,由于CRC-32校验和是4个字节,所以存储校验和的额外开销低于1%。
hadoop提供了一个ChecksumFileSystem,这个类继承自FileSystem类,它的主要作用就是通过使用装饰模式为其他文件系统加入校验和模块。
3.2、压缩
文件压缩有两大好处:减少存储文件所需要的磁盘空间;加速数据在网络和磁盘上的传输时间。常见的压缩算法如下:
压缩格式 |
工具 |
算法 |
文件扩展名 |
多文件 |
可分割性 |
DEFLATE |
无 |
DEFLATE |
.deflate |
不 |
不 |
gzip |
gzip |
DEFLATE |
.gz |
不 |
不 |
ZIP |
zip |
DEFLATE |
.zip |
是 |
是,在文件范围内 |
bzip2 |
bzip2 |
bzip2 |
.bz2 |
不 |
是 |
LZO |
lzop |
LZO |
.lzo |
不 |
是 |
DEFLATE是一个标准压缩算法,该算法的标准实现是zlib。由于没有可用于生成DEFLATE文件的常用命令行工具,因此常用gzip格式。gzip文件格式只是在DEFLATE格式上增加了文件头和文件尾。
既然常用的压缩算法挺多,但既然是压缩算法,肯定需要关注每个压缩算法的磁盘空间压缩比,压缩时间和解压缩时间,下面摘自网上的一个测试。
压缩算法 |
原始文件大小 |
压缩后的文件大小 |
压缩速度 |
解压缩速度 |
gzip |
8.3GB |
1.8GB |
17.5MB/s |
58MB/s |
bzip2 |
8.3GB |
1.1GB |
2.4MB/s |
9.5MB/s |
LZO-bset |
8.3GB |
2GB |
4MB/s |
60.6MB/s |
LZO |
8.3GB |
2.9GB |
49.3MB/S |
74.6MB/s |
在hadoop中,codec代表一种压缩-解压缩算法,一个对CompressionCodec接口的实现代表一个codec。下表列举了hadoop的实现的codec。
压缩格式 |
Hadoop compression codec |
DEFLATE |
org.apache.hadoop.io.compress.DefaultCodec |
gzip |
org.apache.hadoop.io.compress.GzipCodec |
bzip2 |
org.apache.hadoop.io.compress.Bzip2Codec |
LZO |
com.hadoop.compression.lzo.LzoCodec |
CompressionCodec包含两个函数,可以轻松用于压缩和解压缩数据,分别为createOutputStream(OutputStream out)方法和createInputStream(InputStream in)方法。
3.3、压缩与输入分片
上表中“是否可切分”这一列,表示该压缩算法是否支持切分(splitable),也就是说是否可以搜索数据流的任意位置并进一步往下读取数据。可切分压缩格式尤其适合mapreduce(不支持也可以,不过效率慢,整体作为一个map任务的数据)。
在考虑如何压缩将有mapreduce处理的数据时,理解这些压缩格式是否支持切分是非常重要的。
在一个存储在HDFS文件系统上的且压缩前大小为1G的文件为例。如果HDFS块的大小为默认64M,那么该文件将被存储在16个块中,把这个文件作为输入数据的mapreduce作业,如果文件支持切分的话,那么将创建16个数据块,其中每个数据块作为一个map任务的输入;如果文件不支持切发的话,那么一个map任务处理16个HDFS(整体作为输入)。
通常在hadoop中,对于巨大的、没有存储边界的文件,如日志文件,可以考虑如下选项:
- 存储未经压缩的文件
- 使用支持切分的压缩格式,如bzip2
- 使用顺序文件(sequence File),它支持压缩和切分
- 使用一个Avro数据文件,该文件支持压缩和切分,就想顺序文件一样,但增加了许多编程语言都可读写的优势
3.4、hadoop序列化
序列化:是指将结构化对象转化为字节流,以便在网络上传输或写到磁盘上进行永久存储。
反序列化:是指将字节流转向结构化对象的逆过程。
序列化在分布式数据处理量大领域经常出现:进程通信和永久存储。
Hadoop中,各个节点的通信是通过远程调用(RPC)实现的,RPC将数据序列化成二进制后发送给远程节点,远程节点收到数据后将二进制字节流反序列化为原始数据。序列化在RPC应用中有着自己的特点,RPC序列化的特点是:
Hadoop使用自己的序列化格式Writable,它格式紧凑,速度快,但是很难用java以外的语言进行扩展和使用。因为Writable是hadoop的核心(大多数mapreduce程序都会为键和值使用它)。
hadoop提供了大多数java基本类型的writable封装器,使其可以在底层处理序列化数据。
3.4.1序列化框架
在hadoop中,提供了一个可以替换的序列化框架的API。一个序列化框架用一个Serialization(在org.apache.hadoop.io.serializer包中)实现来表示。例如WritableSerialization类是对Writable类型的Serialization实现。
Serialization对象定义了从类型到Serializer(对象到字节流)和Deserializer(字节流到对象)实例的映射方式。
将io.serizalizations属性设置为一个由句点分割的类名列表,即可注册Serialization实现。它的默认值是org.apache.hadoop.io.serializer.WritableSerializationg,这意味着只有Writable对象才可以在外部序列化和反序列化。
虽然在hadoop中默认的序列化框架为WritableSerialization,但hadoop还是提供了java本身自带的JavaSerialization类的框架,该类使用java Object Serialization。
3.4.2、arvo
apache avro是一个独立于编程语言的数据序列化系统,该项目是由hadoop之父创建的,旨在解决hadoop中Writable类型的不足:缺乏语言的可移植性。
avro可以被多种语言(c,c++,java)处理的数据格式,具有丰富的数据类型和模式,主要包括avro模式(定义数据结构)和avro对象容器文件(存储数据)。
3.4.3、SequenceFile
hadoop提供了一种顺序文件类型即SequnceFile,里面存放的其实是键值对数据类型,但这里的键值对都可用二进制数据来表示,因此SequenceFile对于处理二进制数据非常合适。
SequenceFile同样也可以作为小文件的容器,即key保存文件名,value存储文件内容,这样可以把许多小文件合并到一个大文件中,尤其适合hadoop处理大文件。
同样提供了MapFile,其实就是已经排序的SequenceFile,并且加入用于搜索键的索引。可以将MapFile视为java.util.Map的持久化形式。MapFile在保存到磁盘上后,会有两个文件,一个数据原数据文件,另一个是index索引文件。