HDFS
首先先介绍HDFS,这个是整个Hadoop系统的基础组成部分。
介绍
HDFS的设计就是为了解决大数据集合存储的可靠性,并且使这些数据能够以高带宽提供给读者。在一个很大的cluster内部,上千台机器不仅自己存储数据并且执行任务task。用分布式系统的好处之一就是,资源会随着需求在一个合理的范围内增长。
Hadoop提供了分布式文件系统和为MapReduce模型使用大分析和转移大数据集框架。Hadoop的一个特征就是数据大分布(data partition)和搭建在不同主机上大计算,以及将应用的并行计算执行尽量靠近数据。(这里我猜测是物理上的靠近,以次减少传输数据所花费大时间)。
Hadoop是一个Apache开源项目,Yahoo!贡献了Pig、ZooKeeper和Chukwa,以及超过80%的HDFS、MapReduce。Powerset(被微软收购为一个部门)开发了HBase,FB开发了Hive。
HDFS是Hadoop的文件系统。它将元数据metadata和应用数据application data分开存储。HDFS将元数据存储在一个特殊的服务器NameNode上,而应用数据存储在DataNode的服务器上。所有的服务器都是完全连接。
HDFS的DataNode并没有用类似于RAID的方式来保证数据可靠性,而是用了类似GFS的技术,将一份文件的内容存储在多个DataNode上面来保证耐用性。而且这个机制除了保证耐用性,还能数据传输的带宽(???),每个计算也更有可能靠近数据。
不同的文件系统都有实现namespace。Ceph有namespace server(MDS)的集群。GFS有几百个namespace servers(master),而每个master都会有1亿的文件。
框架
NameNode
HDFS的namespace是文件和目录的层级结构。文件和目录都以索引节点inode的形式存储,记录比如permission,修改和访问时间,namespace和硬盘分配的内容。文件被分成了block(比如128MB,可以自定义)。NameNode维护一个namespace树和file block到NameNode的映射。当client读一个文件的时候,首先通过NameNode找到这个file所有block的位置,然后从距离client比较近的DataNode读取数据。当写数据的时候,client会要求NameNode置顶三个DataNode来存储数据(存三份)。cluster可以有一个NameNode,几千个DataNode和上万个HDFS client,而每个DataNode可以同时执行多个任务。
HDFS将整个namespace存放在RAM中。索引节点数据和每个文件由哪些block组成的list,这些命名系统的元数据都叫做image。image存储在本机文件系统的永久数据叫做checkpoint。NameNode将image的修改日志存储在本地叫journal的文件中。为了提高耐用性,checkpoint和journal可以冗余地存储到其他服务器上。重启的时候,NameSpace可以通过读取namespace和重跑journal来恢复namespace。
DataNode
DataNode中的每个file block都是有两个文件组成。第一个包含了数据本身,第二个则包含了这个block的元数据,包括这段data的checksum以及generation stamp。data file的文件大小就是block的大小,比如block如果是10M,那这个data file就是10M,并不需要想传统的文件系统那样把整个128M的block都占用。(但是和前面文件被分成128M矛盾了?)
启动一个DataNode的时候,它都会和NameNode就行handshake,目的是为了确认namespace ID和DataNode的软件版本。任意一个不匹配,DataNode就会关闭。
当这个文件系统建立的时候,namespace ID会在分配给这个系统。这个ID会固定存储在cluster中的每台机器上,不同的namespace ID节点无法加入这个cluster中,由此保持了文件系统的完整性。
软件版本的重要性体现在,如果版本不一致,那么会引起数据的损坏和丢失。
一个刚刚初始化并且没有分配namespace ID的DataNode可以加入cluster并且分配到对应的namespace ID。
在handshake过后,DataNode注册到了NameNode上。DataNode还会保存自己唯一的storage ID,这个ID是每个cluster内部唯一标识的ID,使得将来重启IP或者port改变之后,还能认出。storage ID一旦第一次注册之后,就不再改变。
关于block replica,DataNode会通过给NameNode发送block report来标识。一个block report包含了block id,generation stamp和每个block的长度。第一个block report是在注册之后立即发送,后续的就会每一个小时给NameNode发。
DataNode通过心率活动heartbeat来给NameNode确认自己还在正常运转,以及那些block replica还在。默认的heartbeat每3秒发送一次,前面的block replica是每1小时跟新。如果NameNode在10分钟内没有收到DataNode的heartbeat,就认为这个DataNode宕了,然后讲这台宕机的DataNode的block再复制到其他DataNode。heartbeat发送的内容还包含硬盘存储容量(这个在后面资源分配会提到),使用中的storage比例,和当前数据传输的个数(就有几个数据传输在执行)。这些信息都用来NameNode的空间分配和负载均衡决策。
NameNode并没有直接给DataNode发送信息,而是通过回复DataNode发送的heartbeat来传递指示。这些指示包含这些命令:
- 复制一个block到另外一个节点中
- 将本地block replica删除
- 重注册或者关闭node
- 立即发送一个block report 在不影响其他NameNode操作的前提下,NameNode每秒钟可以执行上千个heartbeat。
HDFS Client
用户应用通过HDFS Client来访问文件系统,这个client是一个可以导出HDFS接口的代码库。
和传统的文件系统一样,HDFS也支持读写删文件,以及创建、删除文件夹的操作。用户应用通过namespace的路径来找到文件和目录。并且这个接口是封装完善的,用户不需要知道系统元数据和数据存储在不同的服务器上,也不需要知道每个block有多个replica。
当应用读一个文件的时候,HDFS先要找到存储这个文件block的DataNode所对应的NameNode,然后直接和NameNode连接,说要把数据从block转移出来。当应用写文件时,它会先让NameNode选择DataNode来复制数据,client控制数据从节点到节点的流向再发送数据。当第一个block填满了,client会请求一个新的DataNode来放下一个block,这就是一条新的流向。每个DataNode的选择都可能不一样。
和传统文件系统不一样的是HDFS提供了API来找到file block的地址,这就让类似MapReduce的应用能够把一个task放到数据所在的服务器,因此提升读取数据的性能。它也允许用于自己设置文件复制次数,默认是3,对于一些比较重要、经常访问的文件,设置比较高的复制数目可以提高容错率fault tolerance,和提高读的带宽。
Image and Journal
namespace image是文件系统的元数据,它将整个应用数据组织成了一个目录结构。checkpoint就是将某个时刻的image写到了硬盘上。journal就是将系统恢复到当前image所必须遵循的commit操作。对每一个用户发起的事务,所有的变化都记录在journal里面,而journal文件会在commit给HDFS之前flush和同步。checkpoint不会被NameNode修改,只是在重启的时候,当一个新的checkpoit被创建时,会按需要代替原来的。
如果checkpoint或者journal丢失或者损坏,namespace信息就会部分或者全部的丢失。为了保存这些重要信息,HDFS会把checkpoint和journal保存在几个存储目录下。推荐的应用就是吧他们放在不同volume的目录和一个在远程NFS服务器的目录下。前者是防止单个volume的损坏,后者是防止整个节点宕。如果NameNode在把journal写到某个目录时遇到了错误,它会自动从存储目录去掉这个目录。
NameNode是一个多线程系统,可以同事处理多个client的请求。讲事务保存到disk变成了瓶颈,因为其他所有的线程都要等某一个线程的flush-and-sync操作完成才能执行。为了优化这个过程,NameNode batch几个不同用户的事务。当NameNode的某一个线程初始化了flush-and-sync,这个batch内的所有事务都开始。剩下的线程只需要检查是否事务以及保存,而不需要初始化flush-and-sync。
CheckpointNode
NameNode除了处理client的请求,还会担任CheckpointNode或者BackupNode的角色。具体承担哪一个会在node启动的时候说明。
CheckpointNode定时的将已经有的checkpoint和journal整合到一起,并创建新的checkpoint和空的journal。(journal的理解就可以是从最新的checkopoint到当前所需要的不同操作日志)CheckpointNode通常是跑不同的NameNode,从NameNode上面下载当前的checkpoint和journal,将它们合并,再把新的checkpoint返回给NameNode。
这种间歇性穿件checkpoint就是一种保护系统元数据的方式。系统可以从最近的checkpoint恢复。一个HDFS系统因为长时间允许,会导致journal非常大,从而是的恢复时间也非常漫长,而且journal文件的损坏和丢失概率也会增加。对于一个大型集群,要花费将近1小时的时间来运行记录了一周事务的journal。一个常用的做法就是每天更新checkpoint。
BackupNode
BackupNode和CheckpointNode很像,也是定期地创建checkpoint,但它会额外在内存里保存一个最新的namespace image。
BackupNode从NameNode接受一个journal流,(这些journal都是关于namespace的操作)保存到本地目录,并将这些事务在内存中跑,生成一个自己的image。如果NameNode宕了,那么BackupNode内存中的image和硬盘上的checkpiont就是最新的namespace。
BackupNode不用专门从NameNode下载checkpoint和journal,因为它一直在维护更新image,这也使得BackupNode上的checkpoint进程更加高效,因为它只需要存储namespace到本地。不用像checkpoint一样下载checkpoint和journal,但消耗一定的(实时)计算资源。
显然BackupNode和NameNode的内存需求是一样的。
BackupNode可以看做是一个只读的NameNode,包含了所有HDFS的元数据,除了block的位置。他可以支持NameNode任意操作,除了和block位置相关的。BackupNode不需要永久存储,而NameNode也会把namespace的状态持久化任务交给BackupNode。
Upgrades, File System Snapchots
文件读写操作和复制replica管理
File Read and Write
HDFS不支持数据的删除,只能增加。它实现了一种single-writer,multiple-reader的模式。
HDFS对每个clien保证了一个租期lease:当某个文件在写的时候,其他client不能写到这个文件中。这个写的client会不定期的发送heartbeat来跟新状态;当它关闭文件时,这个lease会被激活。
HDFS文件是由block组成,当需要一个新的block时,NameNode分配一个block和一个唯一的block ID,并确定哪几个DataNode保存这个block的复制。DataNode形成一个数据流,数据以包packet流的方式输送。当一个包缓冲packet buffer填满时,它就会被输出到pipeline中。
数据被写到HDFS中,知道文件关闭用户才能看到数据。如果要边传输数据,边看到,就要用到hflush()的操作。当前一个packet被放到了pipeline中,hflush会等到所有的DataNode确认收到了这个packet再传下一个。
cluster中的节点每天都会发生。HDFS生成、保存每个data block的checksum。checksum会被client确认,来以此检查有client、DataNode或者忘了产生的数据损坏。当一个client创建了一个HDFS文件,它也会计算每个block的checksum,并发送给DataNode(这就应该是存储在每个block的元数据文件内)。当HDFS读取文件时,每个block的data和checksum都传给了client,client根据收到的数据计算checksum,并进行匹配。如果不匹配,那就是数据有问题,会取一个不同的replica,直到checksum验证通过。
当一个client打开文件读取的时候,它会从NameNode拿到一个block链表,和每个block replica的位置。block的位置是按照它们到这个reader的距离排序的,都是从最近的replica开始,如果当前replica失败,就下一个。如果所有的replica都不可读,那么这个read就会失败。
如果读一个在写的文件,而最后一个block的长度对NameNode还未知,那么client会要求某一个replica给在这个read之前最新的文件长度。
HDFS的读写设计是特地为batch运行优化的,比如MapReduce,他需要对应流读写有很高的吞吐量。大量的工作都用来提高读写响应时间,(比如多用内存)来支持类似Scribe这类需要实时给HDFSHBase提供数据流的应用,或者类似HBase提供对大表格实时随机访问。
Block Placement
Appendix
大部分内容都是参考这篇由Yahoo!的Konstantin,Hairong,Sanjay,和Rober发的 The Hadoop Distributed File System