HDFS架构
前提和设计目标
- 硬件错误
- 硬件错误是常态而不是异常。
- HDFS可能由成百上千的服务器所构成,单机故障概率的存在意味着总有一部分服务器不工作的。
- 错误检测和快速自动恢复是HDFS最核心架构目标。
- 流式数据访问
- 运行在HDFS上的应用需要流式访问它们的数据集。
- HDFS的设计重点是批处理,而不是交互处理。是高吞吐量而不是低延迟。
- 为了提高数据的吞吐量,在关键方面修改POSIX的语义。
- 大规模数据集
- HDFS上的一个典型文件大小一般都在G字节至T字节。MB GB TB PB ZB
- HDFS支持大文件存储。
- 单一HDFS实例能支撑数以千万计的文件。
- 简单的一致性模型
- HDFS应用遵循“一次写入多次读取”的文件访问模型。
- 简化了数据一致性问题,并且使高吞吐量的数据访问成为可能。
- Map/Reduce应用或者网络爬虫应用都非常适合这个模型。
- 移动计算比移动数据更划算
- 降低网络阻塞的影响,提高系统数据的吞吐量。
- 将计算程序发送到数据所在的主机,比GB级别TB级别的数据移动更便捷。
- 异构软硬件平台间的可移植性
- HDFS在设计的时候就考虑到平台的可移植性。
- 这种特性方便了HDFS作为大规模数据应用平台的推广
HDFS架构剖析
问题:100台服务器,存储空间单个200GB总共20T,5T文件如何存储?
128MB一块 128MB*8=1GB 128*8*1024=1TB
5T数据分成的128MB的块数8192*5
清单: 5TB文件分的块: 元数据:
文件名称:web.log 大小:5TB 创建时间: 权限: 文件所有者: 文件所属的用户组: 文件类型:
文件块列表信息:
0~128*1024*1024 -1:128MB:node1:path,node3:path,node8:path
128*1024*1024~2*128*1024*1024 -1:128MB:node2:path,...
2*128*1024*1024~3*128*1024*1024 -1:128MB:node3:path
...
存储模型
- 文件线性按字节切割成块(block),具有offset,id
- 不同文件的block大小可以不一样
- 一个文件除最后一个block,其他block大小一致
- block的大小依据硬件的I/O特性调整
- block被分散存放在集群的节点中,具有location
- block具有副本(replication),没有主从概念,副本不能出现在同一个节点
- 副本是满足可靠性和性能的关键
- 文件上传可以指定block大小和副本数,上传后只能修改副本数
- 一次写入多次读取,不支持修改
- 支持追加数据
namenode中存储的是元数据,文件全路径名、副本数、每个副本的块、块在哪个节点及在节点中的位置
架构设计
- HDFS是一个主从(Master/Slaves)架构
- 由一个NameNode和一些DataNode组成
- 面向文件包含:文件数据(data)和文件元数据(metadata)
- NameNode负责存储和管理文件元数据,并维护了一个层次型的文件目录树
- DataNode负责存储文件数据(block块),并提供block的读写
- DataNode与NameNode维持心跳,并汇报自己持有的block信息
- Client和NameNode交互文件元数据和DataNode交互文件block数据
HDFS角色分析
NameNode
- 完全基于内存存储文件元数据、目录结构、文件block的映射
- 需要持久化方案保证数据可靠性
- 提供副本放置策略
NameNode管理文件系统的命名空间
-
文件和目录的元数据:(运行时,元数据放内存);例如:文件的block副本个数、修改和访问的时间、访问权限、block大小以及组成文件的block信息列表
-
以两种方式在NameNode本地进行持久化:命名空间镜像文件(fsimage)和编辑日志(edits log)
-
fsimage文件不记录每个block所在的DataNode信息,这些信息在每次系统启动的时候从DataNode重建。之后DataNode会周期性地通过心跳包向NameNode报告block信息。DataNode向NameNode注册的时候NameNode请求DataNode发送block列表信息。
1、文件名称和路径 2、文件的大小 3、文件的所属关系 4、文件的block块大小 128MB
5、文件的副本个数 3
6、文件的修改时间 7、文件的访问时间 8、文件的权限 9、文件的block列表
blk1:0,134217728,node1,node13,node26,node1001
blk2:134217728,134217728*2,node7,node89,node1002
blk3:134217728*2,134217728*3,node7,node89,node1002
...
存储结构
一个运行的NameNode如下的目录结构,该目录结构在第一次格式化的时候创建
如果属性dfs.namenode.name.dir指定了多个路径,则每个路径中的内容是一样的,尤其是当其中一个是挂载的NFS的时候,这种机制为管理提供了一些弹性。备份数据
- in_use.lock文件用于NameNode锁定存储目录。这样就防止其他同时运行的NameNode实例使用相同的存储目录
- edits表示edits log日志文件
- fsimage表示文件系统元数据镜像文件
- NameNode在checkpoint之前首先要切换新的edits log文件,在切换时更新seen_txid的值。上次合并fsimage和editslog之后的第一个操作编号
- VERSION文件是一个Java的属性文件
VERSION文件
- layoutVersion是一个负数,定义了HDFS持久化数据结构的版本。这个版本数字跟hadoop发行的版本无关。当layout改变的时候,该数字减1(比如从-57到-58)。当对HDFDS进行了升级,就会发生layoutVersion的改变
- namespaceID是该文件系统的唯一标志符,当NameNode第一次格式化的时候生成
- clusterID是HDFS集群使用的一个唯一标志符,在HDFS联邦的情况下,就看出它的作用了,因为联邦情况下,集群有多个命名空间,不同的命名空间由不同的NameNode管理
- blockpoolID是block池的唯一标志符,一个NameNode管理一个命名空间,该命名空间中的所有文件存储的block都在block池中
- cTime标记着当前NameNode创建的时间。对于刚格式化的存储,该值永远是0,但是当文件系统更新的时候,这个值就会更新为一个时间戳
- storageType表示当前目录存储NameNode内容的数据结构
edits文件
当文件系统客户端进行了写操作(例如创建或移动了文件),这个事务首先在edits log中记录下来。NameNode在内存中有文件系统的元数据,当edits log记录结束后,就更新内存中的元数据。内存中的元数据用于响应客户端的读请求
edits log在磁盘上表现为一定数量的文件。每个文件称为片段(Segment),前缀“edits”,后缀是其中包含的事务ID(transaction IDs)。每个写操作事务都仅仅打开一个文件(比如:edits_inprogress_00000000000010),写完后冲刷缓冲区并同步到磁盘,然后返回客户端success状态码。如果NameNode的元数据需要写到多个目录中,则对于每个写事务需要所有的写操作都完成,并冲刷缓冲区同步到磁盘才返回success状态码。这样就可以保证在发生宕机的时候没有事务数据丢失。
用户的操作是一个事务,每个操作NN都要先将操作记录到edits log中,如果给NN指定了多个目录,则在多个目录中都存在edits log文件,用户的操作要在多个目录中都写完成,才让NN同步数据到内存中。当NN在内存中也同步了数据,就返回客户端success
fsimage文件
每个fsimage文件都是系统元数据的一个完整的持久化检查点(checkpoint)(后缀表示镜像中的最后一个事务)。写操作不更新这个文件,因为镜像文件通常为GB数量级,写到磁盘很慢。如果NameNode宕机,可以将最新fsimage加载到内存,同时执行edits log对应于该fsimage之后的操作,就可以重建元数据的状态。而这正是每次启动NameNode的时候NameNode要做的工作。
SecondaryNameNode
存在的意义
edits log会随着对文件系统的操作而无限制地增长,这对正在运行的NameNode而言没有任何影响,如果NameNode重启,则需要很长的时间执行edits log的记录以更新fsimage(元数据镜像文件)。在此期间,整个系统不可用。
在系统启动期间,NameNode合并fsimage+edits log
fsimage=0, edist log=很大
内存
fsimage=GB
edits log
内存->执行edits log条目
解决方案就是运行SecondaryNameNode,它的作用就是为NameNode内存中的文件系统元数据生成检查点(checkpoint),周期性完成对NN的EditLog向FsImage合并,减少EditLog大小,减少NN启动时间
工作流程
edits_inprogress_00000000018_0000000028 seen_txid=29
- Secondarynamenode请求namenode生成新的edits log文件并向其中写日志。NameNode会在所有的存储目录中更新seen_txid文件
- SecondaryNameNode通过HTTP GET的方式从NameNode下载fsimage和edits文件到本地。
- SecondaryNameNode将fsimage加载到自己的内存,并根据edits log更新内存中的fsimage信息,然后将更新完毕之后的fsimage写到磁盘上
- SecondaryNameNode通过HTTP PUT将新的fsimage文件发送到NameNode,NameNode将该文件保存为.ckpt的临时文件备用
- NameNode重命名该临时文件并准备使用。此时NameNode拥有一个新的fsimage文件和一个新的很小的edits log文件(可能不是空的,因为在SecondaryNameNode合并期间可能对元数据进行了读写操作)。管理员也可以将NameNode置于safemode,通过hdfs dfsadmin -saveNamespace命令来进行edits log和fsimage的合并
SecondaryNameNode要和NameNode拥有相同的内存。对大的集群,SecondaryNameNode运行于一台专用的物理主机
检查点创建时机
对于创建检查点(checkpoint)的过程,有三个参数进行配置:
- 默认情况下,SecondaryNameNode每个小时进行一次checkpoint合并。由dfs.namenode.checkpoint.period设置,单位秒,默认3600s,1小时合并一次
- 在不足一小时的情况下,如果edits log存储的事务达到了1000000个也进行一次
- 事务数量检查默认每分钟进行一次,由dfs.namenode.checkpoint.check.period设置,单位秒
总结
namenode 管理文件元数据 文件名称、大小、所属关系、权限、块大小、副本个数 文件块的列表信息:(块的ID,偏移量,块的大小,块所在的主机名称列表) 持久化文件 fsimage(内存快照),edits log fsimage很大,GB级别;edits log只追加的文件 用户操作首先记录到edits log中,然后更新内存 fsimage不保存数据块位置信息 在系统启动的时候,datanode向namenode发送文件块列表信息(bid) datanode通过心跳向namenode汇报列表信息。 namenode元数据正常工作时,元数据放内存,高并发。 secondarynamenode 在系统启动的时候,namenode首先加载fsimage,然后逐条执行edits log中的日志操作,如果edits log很大,则需要很长时间才能加载完毕,向磁盘写新的fsimage,之后才可以对外提供服务。 周期性地从namenode拷贝fsimage+edits log,在SNN中合并为新的fsimage,推送给namenode。 条件:1、每小时一次,2、不足一小时,则只要edits log中记录的事务数达到了1000000,则合并。
存储结构
-
SecondaryNameNode中checkpoint目录布局(dfs.namenode.checkpoint.dir)和NameNode中的一样(dfs.namenode.name.dir)
-
如果NameNode完全坏掉(没有备用机,也没有NFS),可以快速地从SecondaryNameNode恢复。有可能丢数据
-
如果SecondaryNameNode直接接手NameNode的工作,需要在启动NameNode进程的时候添加-importCheckpoint选项。该选项会让NameNode从由dfs.namenode.checkpoint.dir属性定义的路径中加载最新的checkpoint数据,但是为了防止元数据的覆盖,要求dfs.namenode.name.dir定义的目录中没有内容
DataNode
- 基于本地磁盘存储block(文件的形式)
- 并保存block的校验和数据保证block的可靠性
- 与NameNode保持心跳,汇报block列表状态
存储结构
DataNode不需要显式地格式化;关键文件和目录结构如下:
-
HDFS块数据存储于blk_前缀的文件中,包含了被存储文件原始字节数据的一部分
-
每个block文件都有一个.meta后缀的元数据文件关联。该文件包含了一个版本和类型信息的头部,后接该block中每个部分的校验和
-
每个block属于一个block池,每个block池有自己的存储目录,该目录名称就是该池子的ID(跟NameNode的VERSION文件中记录的block池ID一样)
当一个目录中的block达到64个(通过dfs.datanode.numblocks配置)的时候,DataNode会创建一个新的子目录来存放新的block和它们的元数据。这样即使当系统中有大量的block的时候,目录树也不会太深。同时也保证了在每个目录中文件的数量是可管理的,避免了多数操作系统都会碰到的单个目录中的文件个数限制(几十几百上千个)
如果dfs.datanode.data.dir指定了位于在不同的硬盘驱动器上的多个不同的目录,则会通过轮询的方式向目录中写block数据。需要注意的是block的副本不会在同一个DataNode上复制,而是在不同的DataNode节点之间复制
存储数据模型
- 文件线性切割成块(Block)(按字节切割)
- Block分散存储在集群节点中
- 单一文件Block大小一致,文件与文件可以不一致
hdfs dfs -D dfs.blocksize=1048576 -D dfs.replication=2 -put hello.txt /
- Block可以设置副本数,副本分散在不同节点中。副本数不要超过节点数量、承担计算、容错
- 文件上传可以设置Block大小和副本数
- 已上传的文件Block副本数可以调整,大小不变
- 只支持一次写入多次读取,同一时刻只有一个写入者。对同一个文件,一个时刻只有一个写入者
- 可以append追加数据
优势
- 一个文件的大小可以大于网络中任意一个磁盘的容量
- 使用抽象块而非整个文件作为存储单元,大大简化存储子系统的设计
- 块非常适合用于数据备份进而提供数据容错能力和提高可用性
元数据持久化
内存数据持久化一般有两种方式,日志文件和镜像。
日志文件:记录实时发生的增删改的操作然后追加到文本文件,日志文件完整性比较好(一般不丢失数据);但是日志文件的大小可能会膨胀到很大,这样会导致加载恢复数据较慢。比如NameNode内存4G,运行了10年,日志文件会很大(如果不处理),这样恢复时间会很长,恢复时内存可能还会溢出
镜像:又称为快照、dump、db、序列化等;每间隔(1天、1小时、10分钟、1分钟、5秒钟)一段时间,将基于某一个时间点的内存全量数据溢写到磁盘,是基于二进制存储的比全量的文本文件小。镜像恢复速度快过日志文件,因为是间隔的,容易丢失一部分数据
内存数据持久化一般会结合使用镜像和日志文件,定期将内存数据镜像到磁盘并开启新的日志文件,然后删除老的镜像和日志文件。这样保证了日志文件不会膨胀到很大,恢复时间可控。这样同时发挥了镜像文件较小恢复快,日志文件不丢失数据的优点;同时避免了二者的缺点,日志文件不会膨胀到很大,不用频繁地创建镜像文件。
NameNode元数据持久化就结合使用镜像和日志文件,并做了自己的优化
- 任何对文件系统元数据产生修改的操作,Namenode都会使用一种称为EditLog的事务日志记录下来
- 使用FsImage存储内存所有的元数据状态
- 使用本地磁盘保存EditLog和FsImage
- EditLog具有完整性,数据丢失少,但恢复速度慢,并有体积膨胀风险
- FsImage具有恢复速度快,体积与内存数据相当,但不能实时保存,数据丢失多
- NameNode使用了FsImage+EditLog整合的方案:滚动将增量的EditLog更新到FsImage,以保证更近时点的FsImage和更小的EditLog体积(最近时点的FsImage + 增量的EditsLog,就可以恢复内存到关机前的全量数据状态)
NameNode第一次开机的时候,写一次FsImage,后面的FsImage都是增量更新的。假设8点到9点的时候,EditLog记录的是89点的日志,只需要将89点的日志记录,更新到8点的FsImage中,FsImage的数据时点就变成了9点。集群比较繁忙时NameNode压力会较大,一般会启动SecondaryNameNode来增量更新FsImage,然后再发送给NameNode
NameNode存元数据:文件属性、每个块存在哪个DataNode上。在持久化的时候,文件属性会持久化,但是文件的每一个块位置信息不会持久化。恢复的时候,DN会和NN建立心跳,汇报块信息,NN不会丢失块的位置信息。如果NameNode也存储块的位置信息,这样容易造成数据的不一致(分布式系统中数据一致性问题比较复杂)
安全模式
工作流程
当HDFS启动后,根据前面知识的学习,启动后工作流程大致如下:
- HDFS搭建时会格式化,格式化操作会产生一个空的FsImage
- Namenode启动后会进入一个称为安全模式的特殊状态
- 启动NameNode,它从硬盘中读取Editlog和FsImage,将所有Editlog中的事务作用在内存中的FsImage上(进行FsImage和Editlog日志的合并)
- 将这个新版本的FsImage从内存中保存到本地磁盘上(创建新的FsImage文件),然后创建一个空的Editlog并删除旧的Editlog
- NameNode等待DataNode上传block列表信息,直到副本数满足最小副本条件,最小副本条件指整个文件系统中有99.9%(可配置)的block达到了最小副本数(默认值是1,可设置),数据块副本数达到最小副本数就会被认为是副本安全的(safely replicated)
- 当满足了最小副本条件,再过30秒,NameNode就会退出安全模式
在NameNode安全模式(safemode)下,操作HDFS有如下特点
- 对文件系统元数据进行只读操作
- 当文件的所有block信息具备的情况下,对文件进行只读操作。不允许进行文件修改(写,删除或重命名文件)
- Namenode是不会进行数据块的复制的(推出安全模式后,接下来Namenode会确定还有哪些数据块的副本没有达到指定数目,并将这些数据块复制到其他Datanode上)
注意事项
- NameNode不会持久化block位置信息,DataNode保有各自存储的block列表信息。正常操作时,NameNode在内存中有一个blocks位置的映射信息(所有文件的所有文件块的位置映射信息)
- NameNode在安全模式,DataNode需要上传block列表信息到NameNode
- 在安全模式NameNode不会要求DataNode复制或删除block
- 新格式化的HDFS不进入安全模式,因为DataNode压根就没有block
配置信息
属性名称 | 类型 | 默认值 | 描述 |
---|---|---|---|
dfs.namenode.replication.min | Int | 1 | 写文件成功的最小副本数 |
dfs.namenode.safemode.threshold-pct | float | 0.999 | 系统中block达到了最小副本数的比例,之后NameNode会退出安全模式 |
dfs.namenode.safemode.extension | intms | 30000 | 当副本数达到最小副本条件之后安全模式延续的时间。对于小的集群(几十个节点),可以设置为0 |
命令操作
通过以下命令查看namenode是否处于安全模式
hdfs dfsadmin -safemode get
HDFS的前端webUI页面也可以查看NameNode是否处于安全模式,有时候我们希望等待安全模式退出,之后进行文件的读写操作,尤其是在脚本中,此时可以执行命令
hdfs dfsadmin -safemode wait
当然,以上这个命令不会经常使用,更多的是集群正常启动后会自动退出安全模式。管理员有权在任何时间让namenode进入或退出安全模式,进入安全模式命令如下
hdfs dfsadmin -safemode enter
以上命令可以让namenode一直处于安全模式,离开安全模式命令如下
hdfs dfsadmin -safemode leave
数据块副本放置策略
Block的副本放置策略
第一个副本:放置在上传文件的DN;如果是集群外提交,则随机挑选一台磁盘不太满,CPU不太忙的节点
第二个副本:放置在与第一个副本不同的机架的节点上
第三个副本:与第二个副本相同机架的不同节点
更多副本:随机节点
HDFS写文件流程
上图是hdfs客户端写一个block的流程
- Client和NN连接创建文件元数据,NN判定元数据是否有效
- NN触发副本放置策略,返回一个有序的DN列表
- Client和DN建立Pipeline连接(Client和列表中的第一个DN建立TCP连接,第一个DN和第二个DN建立TCP连接,以此类推)
- Client将块切分成packet(64KB),并使用chunk(512B)+chucksum(4B)填充
- Client将packet放入发送队列dataqueue中,并向第一个DN发送
- 第一个DN收到packet后本地保存并发送给第二个DN
- 第二个DN收到packet后本地保存并发送给第三个DN
- 这一个过程中,上游节点同时发送下一个packet。这一过程就像是建立了一个pipeline管道,从上游把数据流到下游,传输数据时不需要等前面的packet传输到所有副本就可以传输下一个packet,不会阻塞(流式其实也是变种的并行计算)
Hdfs使用这种传输方式,副本数对于client是透明的。当block传输完成,DN们各自向NN汇报,同时client继续传输下一个block。所以,client的传输和block的汇报也是并行的
hdfs的java客户端写文件大致流程如下
- 调用客户端的对象DistributedFileSystem的create方法;
- DistributedFileSystem会发起对namenode的一个RPC连接,请求创建一个文件,不包含关于block块的请求。namenode会执行各种各样的检查,确保要创建的文件不存在,并且客户端有创建文件的权限。如果检查通过,namenode会创建一个文件(在edits中,同时更新内存状态),否则创建失败,客户端抛异常IOException。
- NN在文件创建后,返回给HDFS Client可以开始上传文件块。
- DistributedFileSystem返回一个FSDataOutputStream对象给客户端用于写数据。FSDataOutputStream封装了一个DFSOutputStream对象负责客户端跟datanode以及namenode的通信。
- FSDataOutputStream对象将数据切分为小的数据包(64kb,core-default.xml:file.client-write-packet-size默认值65536),并写入到一个内部队列(“数据队列”)。DataStreamer会读取其中内容,并请求namenode返回一个datanode列表来存储当前block副本。列表中的datanode会形成管线,DataStreamer将数据包发送给管线中的第一个datanode,第一个datanode将接收到的数据发送给第二个datanode,第二个发送给第三个...
- DFSOutputStream维护着一个数据包的队列,这些数据包是需要写入到datanode中的,该队列称为确认队列。当一个数据包在管线中所有datanode中写入完成,就从ack队列中移除该数据包。如果在数据写入期间datanode发生故障,则执行以下操作
- 关闭管线,把确认队列中的所有包都添加回数据队列的最前端,以保证故障节点下游的datanode不会漏掉任何一个数据包。
- 为存储在另一正常datanode的当前数据块指定一个新的标志,并将该标志传送给namenode,以便故障datanode在恢复后可以删除存储的部分数据块。
- 从管线中删除故障数据节点并且把余下的数据块写入管线中另外两个正常的datanode。namenode在检测到副本数量不足时,会在另一个节点上创建新的副本。
- 后续的数据块继续正常接受处理。
- 在一个块被写入期间可能会有多个datanode同时发生故障,但非常少见。只要设置了dfs.replication.min的副本数(默认为1),写操作就会成功,并且这个块可以在集群中异步复制,直到达到其目标副本数(dfs.replication默认值为3)。
- 如果有多个block,则会反复从步骤4开始执行。
- 当客户端完成了数据的传输,调用数据流的close方法。该方法将数据队列中的剩余数据包写到datanode的管线并等待管线的确认。
- 客户端收到管线中所有正常datanode的确认消息后,通知namenode文件写完了。
- namenode已经知道文件由哪些块组成,所以它在返回成功前只需要等待数据块进行最小量的复制。
HDFS读文件流程
流程
- 为了降低整体的带宽消耗和读取延时,HDFS会尽量让读取程序读取离它最近的副本
- 如果在读取程序的同一个机架上有一个副本,那么就读取该副本
- 如果一个HDFS集群跨越多个数据中心,那么客户端也将首先读本地数据中心的副本
语义:下载一个文件: Client和NN交互文件元数据获取fileBlockLocation,NN会按距离策略排序返回,Client尝试下载block并校验数据完整性
语义:下载一个文件其实是获取文件的所有的block元数据,那么子集获取某些block应该成立 Hdfs支持client给出文件的offset自定义连接哪些block的DN,自定义获取数据;这个是支持计算层的分治、并行计算的核心
hdfs的java客户端读文件大致流程如下
- 客户端通过FileSystem对象的open方法打开希望读取的文件,DistributedFileSystem对象通过RPC调用namenode,以确定文件起始位置。对于每个block,namenode返回存有该副本的datanode地址。这些datanode根据它们与客户端的距离来排序。如果客户端本身就是一个datanode,并保存有相应block一个副本,会从本地读取这个block数据。
- DistributedFileSystem返回一个FSDataInputStream对象给客户端读取数据。该类封装了DFSInputStream对象,该对象管理着datanode和namenode的I/O,用于给客户端使用。客户端对这个输入调用read方法,存储着文件起始几个block的datanode地址的DFSInputStream连接距离最近的datanode。通过对数据流反复调用read方法,可以将数据从datanode传输到客户端。到达block的末端时,DFSInputSream关闭与该datanode的连接,然后寻找下一个block的最佳datanode。客户端只需要读取连续的流,并且对于客户端都是透明的。
- 客户端从流中读取数据时,block是按照打开DFSInputStream与datanode新建连接的顺序读取的。它也会根据需要询问namenode来检索下一批数据块的datanode的位置。一旦客户端完成读取,就close掉FSDataInputStream的输入流。
- 在读取数据的时候如果DFSInputStream在与datanode通信时遇到错误,会尝试从这个块的一个最近邻datanode读取数据。它也记住那个故障datanode,保证以后不会反复读取该节点上后续的block。DFSInputStream也会通过校验和确认从datanode发来的数据是否完整。如果发现有损坏的块,就在DFSInputStream试图从其他datanode读取其副本之前通知namenode。
- Client下载完block后会验证DN中的MD5,保证块数据的完整性。
注意
namenode告知客户端每个block中最佳的datanode,并让客户端直接连到datanode检索数据。由于数据流分散在集群中的所有datanode,这样可以使HDFS可扩展到大量的并发客户端。同时,namenode只需要响应block位置的请求,无需响应数据请求,否则namenode会成为瓶颈
最近邻
hadoop把网络看作是一棵树,两个节点间的距离是它们到最近共同祖先的距离和。通常可以设置等级:
- 同一个节点上的进程
- 同一机架上的不同节点
- 同一数据中心中不同机架上的节点
- 不同数据中心中的节点
HDFS架构演进
hadoop1.x版本,namenode是主从架构,一个NN一个SNN。主从架构,结构相对简单,主与从协作。只有主对外提供服务,数据一致好掌握,但是存在单点故障,主不可用会导致集群整体不可用。所有元数据都存储在一个NN节点,受到一个NN节点内存的限制,元数据很多时会导致NN压力过大。
hadoop这些缺陷在2.x版本中都得到了解决
- 单点故障:高可用方案(HA High Available),多个NN,主备切换,注意这是主备不是主从
- 压力过大,内存受限:联帮机制,Federation(元数据分片);多个NN,管理不同的元数据
HADOOP 2.x 只支持HA的一主一备,3.x 支持HA的一主多备