主从架构。
以流式数据访问模式来存储超大文件,可以从MB乃至PB级大小,不能行于商用硬件集群上。
是为高吞吐量应用优化的,这可能会以高时延为代价,低时延应选择HBase。
HDFS所能够存储的文件总数受限于NameNode的内存总量。
HDFS中的文件可能只有一个writer,且洗的数据总是将数据添加在文件的末尾。不支持具有多个writer的操作,也不支持在文件的任意位置进行修改(因为这么做效率较低)。
文件系统中的所有块不需要存储在同一个磁盘上,因为可以利用集群上的任意一个磁盘进行存储。但对整个HDFS集群而言,也可以仅存储一个文件,该文件的块占满了集群中的所有磁盘。
流式数据访问
一次写入,多次读取,读取整个数据集的时间延迟比读取第一条记录的时间延迟更重要。
商用硬件
非昂贵,非高可靠,但是数量庞大,节点故障的几率较高,但是节点发生故障仍然能够继续运行不让客户察觉到明显的中断。
名称节点-NameNode-管理者*1
是整个文件系统的管理节点。
1. 维护整个文件系统的文件目录树,管理命名空间。
2. 维护文件、目录的元数据(表1)和每个文件对应的数据块列表(表2),执行文件系统操作。
3. 接收用户的操作请求,规范客户端对文件的访问。
4. 命名空间镜像文件和编辑日志文件以文件的形式永久保存在本地磁盘上。?
5. 运行NameNode的服务器若发生毁坏,则文件系统上的文件将会全部丢失
NameNode容错机制
1. 备份组成文件系统元数据持久状态的文件。可以通过配置使NameNode在多个文件系统上保存元数据的持久状态。一般配置是将持久状态写入本地磁盘的同时,写入一个远程挂载的网络文件系统(NFS)
2. 运行一个辅助的NameNode,但其并不能被用做NameNode。定期通过编辑日志合并命名空间镜像,防止编辑日志过大。一般运行在另一台计算机,因为合并过程相当占用CPU资源。辅助NameNode保存状态落后于主节点,因此启用备份时仍会丢失部分数据。
元数据存放在fsimage中,在运行的时候加载到内存中的(读写比较快)。
操作日志写到edits中。刚开始的写文件会写入到内存中和edits中,edits会记录文件系统的每一步操作,当达到一定的容量会将其内容写入fsimage中。
数据节点-DataNode-工作者*n
提供真实文件数据的存储服务。
是文件系统的工作节点,他们根据需要存储并检索数据块(受客户端或NameNode调度),并且定期向NameNode发送他们所存储的块的列表。
文件块(block),最基本的存储单位,对于文件内容而言,一个文件的长度大小是size,那么从文件的0偏移开始,按照固定大小,书序对文件进行划分编号,划分好的每一个块成为block。
默认的文件块大小为128M(以前为64M),不同于普通文件系统,HDFS的一个文件如果小于一个数据块的大小,并不占用整个数据块的存储空间。
为何设置128M或64M:
1. 减少磁盘寻道时间,io时间远小于磁盘寻道时间,由于数据块非连续存储,读越多的数据块就会花费更多的寻道时间。?不是只读一块而已么,还是说HDFS的一块在磁盘中实际上是若干块。?
2. 减少NameNode的内存消耗,数据块太小的话,同样大小的文件需要的元数据信息就越多,会增加NameNode的内存压力。
3. 数据块越大,数据加载时间越长
4. 数据块越小,判定节点死亡的时间越短。
5. 一个map处理一个块,块太小则运行速度变慢
多副本dfs.replication,默认为3个。
1. 根据客户的需求。在数据节点上的文件系统内执行读写操作
2. 根据名称节点的指令执行操作,如块的创建,删除和复制。
1. create:
客户端通过对DistributedFileSystem(DFS)对象调用create()函数来创建文件。(客户端->DFS)
2. create:
DFS对NameNode创建一个RPC(远程过程调用),在文件系统的命名空间中创建一个新文件,此时该文件中还没有相应的数据块。(DFS->NameNode)
NameNode执行各种不同的检查以确保这个文件不存在,并且客户端有创建改文件的权限。如果这些检查均通过,NameNode就会为创建新文件记录一条记录,否则文件创建失败并向客户端抛出一个IOException。如果成功,则DFS向客户端返回一个FSDataOutputStream对象,客户端可以开始写入数据。(NameNode,DFS(FSDataOutputStream)->客户端)
FSDataOutputStream封装一个DFSOutputStream对象,负责处理DataNode和NameNode之间的通信。
3. write:
客户端写入数据时,DFSOutputStream将它分成一个个数据的处理包,并写入内部队列,称为数据队列。DataStreamer处理数据队列,他的责任是根据DataNode列表来要求NameNode分配合适的新块来存储数据备份。
4.
这一组DataNode构成一个管线,DataStreamer会将数据包流式传输到管线中的第一个DataNode,第一个DataNode存储数据包并且向下传递,第二个同理。
5.
DFSOutputStream也维护这一个内部数据包队列来等待DataNode的收到确认回执,称为确认队列。当管道中所有DataNode确认信息后,该数据包才会从确认队列删除。
如果在数据写入期间,DataNode发生故障,首先关闭管线,确认把队列中的任何数据包都添加回数据队列的最前端,以确保故障节点下游的DataNode不会漏掉任何一个数据包。
为存储在另一正常DataNode的当前数据块指定一个新的标识,并且将该标识传给NameNode,以便故障DataNode在恢复后可以删除存储的部分数据块。
从管线中删除故障数据节点并且将余下的数据块写入管线中的两个正常的DataNode。NameNode注意到块复本量不足时,会在另一个节点上创建一个新的副本,后续的数据块继续正常接受处理。
一个块写入期间可能会有多个DataNode同时发生故障,但非常少见。只要写入了dfs.replication.min的复本数(默认为1),写操作就会成功,并且这个块可以在集群中异步复制,直到其达到目标复本数dfs.replication(默认值为3)
6. close
将剩余所有数据包写入DataNode管线中,并在联系NameNode且发送文件写入完成信号(complete)之前等待确认。
7. complete
NameNode已经知道文件由哪些块组成(通过DataStreamer询问数据块的分配),所以它在返回成功之前只需要等待数据块进行最小量(dfs.replication.min)的复制。
一份文件默认有3个复本,
第一个复本->运行客户端的节点(要求节点在集群内),或是随机节点(要求空间充足且较为空闲)
第二个复本->不同机架且随机选择的节点
第三个复本->与第二复本相同机架但随机选择节点
一致性模型(Coherency Model)
描述了对文件读写的数据可见性。
写入文件的内容并不能保证立即可见,即使数据流已经刷新并存储,只有当写入数据超过一个块之后,新的reader就能看见这个块,当块没有写满或者正在写入,reader是无法发现的。
HDFS提供了FSDataOutputStream中的sync()方法来强制所有缓存与数据节点同步,当sync()返回成功后,对于所有reader而言,HDFS能保证文件中到目前为止写入的数据均可见一致。
HDFS在关闭文件(close)的时候也隐性执行了sync()方法。
如果不调用sync(),客户端或者系统发生故障的时候就可能丢失一部分数据,但是sync()有许多额外开销,因此需要在合适的时候使用。
sync()从hflush()中分离出来,由于数据依旧在磁盘缓冲区中,所以该方法仅仅确保新用户能够看到至目前所有写入的数据;
hsync()则是确保操作系统将刷新的数据写入磁盘。