赞
踩
Hadoop1.0是按64MB切,BlockSize=64MB; Hadoop2.0 BlockSize=128MB
优缺点:
NameNode+DataNode架构模式(主从架构,也叫Master+Slave架构)
负责元数据管理,维护整个文件系统树及树内所有文件和索引目录;本地有fsimage(元数据镜像)和edit log(操作日志),文件系统树存在内存中,每次重启都从DataNode节点收集; 此外为了应对服务宕机而引起的元数据丢失,也要持久化到本地文件里;
初始化元数据:hadoop namenode -format 这个指令实际的作用是创建了初始的fsimage文件和edits文件。
方案如下:
此架构主要解决了两个问题,一是 NameNode 元数据同步问题,二是主备 NameNode 切换问题,由图可知,解决主、备 NameNode 元数据同步是通过 JournalNode 集群来完成的,而解决主、备 NameNode 切换可通过 ZooKeeper 来完成。
ZooKeeper 是一个独立的集群,在两个 NameNode 上还需要启动一个 failoverController(zkfc)进程,该进程作为 ZooKeeper 集群的客户端存在,通过 zkfc 可以实现与 ZooKeeper 集群的交互和状态监测。(ZKFC部署在namenode服务器上)
在hadoop2.0引入了HA机制。hadoop2.0的HA机制官方介绍了有2种方式,一种是NFS(Network File System,共享文件实现两个NN之间的数据同步)方式,另外一种是QJM(Quorum Journal Manager)方式。
从 Hadoop2.x 版本开始,HDFS 开始支持多个 NameNode,这样不但可以实现 HDFS 的高可用,而且还可以横行扩容 HDFS 的存储规模。在实际的企业应用中,使用最多的是双 NameNode 架构,也就是一个 NameNode 处于 Active(活跃) 状态,另一个 NameNode 处于 Standby(备用)状态,通过这种机制,实现 NameNode 的双机热备高可用功能。
在高可用的 NameNode 体系结构中,只有 Active 状态的 NameNode 是正常工作的,Standby 状态的 NameNode 处于随时待命状态,它时刻去同步 Active 状态 NameNode 的元数据。一旦 Active 状态的 NameNode 不能工作,可以通过手工或者自动切换方式将 Standby 状态的 NameNode 转变为 Active 状态,保持 NameNode 持续工作。Active NameNode和Standby NameNode之间通过NFS或者JN(JournalNode,QJM方式)来同步数据。这就是两个高可靠的 NameNode 的实现机制。
双 NameNode 架构中元数据一致性如何保证?
从 Hadoop2.x 版本后,HDFS 采用了一种全新的元数据共享机制,即通过 Quorum Journal Node(JournalNode)集群或者 network File System(NFS)进行数据共享。NFS 是操作系统层面的,而 JournalNode 是 Hadoop 层面的,成熟可靠、使用简单方便,所以,这里我们采用 JournalNode 集群进行元数据共享。(瓜子也是采用JournalNode)
由图可知,JournalNode 集群可以几乎实时的去 NameNode 上拉取元数据,然后保存元数据到 JournalNode 集群;同时,处于 standby 状态的 NameNode 也会实时的去 JournalNode 集群上同步 JNS 数据,通过这种方式,就实现了两个 NameNode 之间的数据同步。(注: HA架构下,没有SecondaryNameNode,而是两个NameNode,通过JournalNode来合并fsimage)
具体原理如下:
JournalNode 集群内部是如何实现的呢?
两个 NameNode 为了数据同步,会通过一组称作 JournalNodes 的独立进程进行相互通信。当 Active 状态的 NameNode 元数据有任何修改时,会告知大部分的 JournalNodes 进程。同时,Standby 状态的 NameNode 也会读取 JNs 中的变更信息,并且一直监控 EditLog (事务日志)的变化,并把变化应用于自己的命名空间。Standby 可以确保在集群出错时,元数据状态已经完全同步了。
JournalNode 集群的内部运行架构图:
由图可知,JN1、JN2、JN3 等是 JournalNode 集群的节点,QJM(Qurom Journal Manager)的基本原理是用 2N+1 台 JournalNode 存储 EditLog,每次写数据操作有 N/2+1 个节点返回成功,那么本次写操作才算成功,保证数据高可用。当然这个算法所能容忍的是最多有 N 台机器挂掉,如果多于 N 台挂掉,算法就会失效。
NameNode 主、备之间的切换可以通过手动或者自动方式来实现,作为线上大数据环境,都是通过自动方式来实现切换的,为保证自动切换,NameNode 使用 ZooKeeper 集群进行仲裁选举。基本的思路是 HDFS 集群中的两个 NameNode 都在 ZooKeeper 中注册,当 Active 状态的 NameNode 出故障时,ZooKeeper 能马上检测到这种情况,它会自动把 Standby 状态切换为 Active 状态。(即利用ZK实现master选举)
在Hadoop HA中,主要由以下几个组件构成:
实现高可用性时,可能会出现的问题:
HA解决的难度取决于Master自身记录信息的多少和信息可重构性,如果记录的信息非常庞大且不可动态重构,比如NameNode,则需要一个可靠性与性能均很高的共享存储系统,而如果Master保存有很多信息,但绝大多数可通过Slave动态重构,则HA解决方法则容易得多,典型代表是MapReduce和YARN。从另外一个角度看,由于计算框架对信息丢失不是非常敏感,比如一个已经完成的任务信息丢失,只需重算即可获取,使得计算框架的HA设计难度远低于存储类系统。(计算型任务的HA很简单,直接重新跑就可以,存储型设计到信息的迁移回复,就比较麻烦,HDFS如此,HBase的RegionServer也是如此)
集群部署规划示例:
部署前主机、软件功能、磁盘存储规划双 NameNode 的 Hadoop 集群环境涉及到的角色有 Namenode、datanode、resourcemanager(yarn)、nodemanager、historyserver、ZooKeeper、JournalNode 和 zkfc,这些角色可以单独运行在一台服务器上,也可以将某些角色合并在一起运行在一台机器上。
一般情况下,NameNode 服务要独立部署,这样两个 NameNode 就需要两台服务器,而 datanode 和 nodemanager 服务建议部署在一台服务器上,resourcemanager (yarn)服务跟 NameNode 类似,也建议独立部署在一台服务器上,而 historyserver 一般和 resourcemanager 服务放在一起。ZooKeeper 和 JournalNode 服务是基于集群架构的,因此至少需要 3 个集群节点,即需要 3 台服务器,不过 ZooKeeper 和 JournalNode 集群可以放在一起,共享 3 台服务器资源。最后,zkfc 是对 NameNode 进行资源仲裁的,所以它必须和 NameNode 服务运行在一起,这样 zkfc 就不需要占用独立的服务器了。
本着节约成本、优化资源、合理配置的原则,下面的部署通过 5 台独立的服务器来实现,操作系统均采用 Centos7.7 版本,每个服务器主机名、IP 地址以及功能角色如下表所示:
由表可知,namenodemaster 和 yarnserver 是作为 NameNode 的主、备两个节点,同时 yarnserver 还充当了 ResourceManager 和 JobHistoryServer 的角色。如果服务器资源充足,可以将 ResourceManager 和 JobHistoryServer 服务放在一台独立的机器上。
此外,slave001、slave002 和 slave003 三台主机上部署了 ZooKeeper 集群、JournalNode 集群,还充当了 DataNode 和 NodeManager 的角色。
在软件部署上,每个软件采用的版本如下表所示:
最后,还需要考虑磁盘存储规划,HDFS 文件系统的数据块都存储在本地的每个 datanode 节点上。因此,每个 datanode 节点要有大容量的磁盘(120T每台),磁盘类型可以是普通的机械硬盘,有条件的话 SSD 硬盘最好(datanode机械硬盘即可,olap才会用ssd,比如kudu+impala,clickhouse),单块硬盘推荐 4T 或者 8T,这些硬盘无需做 RAID,单盘使用即可,因为 HDFS 本身已经有了副本容错机制。(hdfs本身就配置了3副本)
NameNode 节点所在的主机要存储 HDFS 的元数据信息,这些元数据控制着整个 HDFS 集群中的存储与读写,一旦丢失,那么 HDFS 将丢失数据甚至无法使用,所以保证 NameNode 节点 HDFS 元数据安全性至关重要。在 NameNode 节点建议配置 4 块大小一样的磁盘,每两块做一个 raid1,共做两组 raid1,然后将元数据镜像存储在这两个 raid1 上。(dcm:虽然datanode是3副本,但是namenode上的数据,还是需要做raid1,就是完全复制的)
在gz实际项目中部分机器部署情况:
在实际项目中,我们还采用RBF解决了NameNode元数据太多的问题,具体参见相关章节。
负责块数据的管理,存储文件的节点,定时向NameNode同步存储块的列表;为了防止DataNode挂掉造成的数据丢失,对于文件块要有备份,一个文件块有三个副本(共3份)。(元数据管理在分布式构架中,是典型设计模式,分布式必有元数据管理,比如kafka,clickhouse,rabbitmq等等)
磁盘block一般为512Byte;文件系统block一般为n*512Byte,一般几K;HDFS的block默认为64M,hadoop2新版本为128M;但若果一个1M的文件实际上只占用1M,并不会占用128M。
文件块(block):最基本的存储单位。对于文件内容而言,一个文件的长度大小是size,那么从文件的0偏移开始,按照固定的大小,顺序对文件进行划分并编号,划分好的每一个块称一个Block。HDFS默认Block大小是128MB,以一个256MB文件,共有256/128=2个Block。几个关键参数如下:
dfs.blocksize(我看了一下hadoop3的配置,还是128M)
不同于普通文件系统的是,HDFS中,如果一个文件小于一个数据块的大小,并不占用整个数据块存储空间。
Replication:多副本。默认是三个。
HDFS的Block为什么这么大?(总时长= 定位时长 + 传输时长,找到平衡)
是为了最小化查找(seek)时间,控制定位文件与传输文件所用的时间比例。假设定位到Block所需的时间为10ms,磁盘传输速度为100M/s。如果要将定位到Block所用时间占传输时间的比例控制1%,则Block大小需要约100M。(本质是索引,稀疏索引,文件分块存储,在索引和数据传输中找到平衡点)
但是如果Block设置过大,在MapReduce任务中,Map或者Reduce任务的个数如果小于集群机器数量,会使得作业运行效率很低。(MAP任务数和数据块相关)
Block抽象的好处(逻辑抽象,物理介质可以是任意的;且可以分布在任意地方,不受单机限制)
注意:block虽然128M,但是实际占用空间为文件真实大小
HDFS读过程
1.初始化FileSystem,然后客户端(client)用FileSystem的open()函数打开文件
2.FileSystem用RPC调用元数据节点,得到文件的数据块信息,对于每一个数据块,元数据节点返回保存数据块的数据节点的地址。
3.FileSystem返回FSDataInputStream给客户端,用来读取数据,客户端调用stream的read()函数开始读取数据。
4.DFSInputStream连接保存此文件第一个数据块的最近的数据节点,data从数据节点读到客户端(client)
5.当此数据块读取完毕时,DFSInputStream关闭和此数据节点的连接,然后连接此文件下一个数据块的最近的数据节点。
6.当客户端读取完毕数据的时候,调用FSDataInputStream的close函数。
7.在读取数据的过程中,如果客户端在与数据节点通信出现错误,则尝试连接包含此数据块的下一个数据节点。
8.失败的数据节点将被记录,以后不再连接。
(2)写过程
1.初始化FileSystem,客户端调用create()来创建文件
2.FileSystem用RPC调用元数据节点,在文件系统的命名空间中创建一个新的文件,元数据节点首先确定文件原来不存在,并且客户端有创建文件的权限,然后创建新文件。
3.FileSystem返回DFSOutputStream,客户端用于写数据,客户端开始写入数据。
4.DFSOutputStream将数据分成块,写入data queue。data queue由Data Streamer读取,并通知元数据节点分配数据节点,用来存储数据块(每块默认复制3块)。分配的数据节点放在一个pipeline里。Data Streamer将数据块写入pipeline中的第一个数据节点。第一个数据节点将数据块发送给第二个数据节点。第二个数据节点将数据发送给第三个数据节点。(写入到第一个节点,节点自己复制3份)
5.DFSOutputStream为发出去的数据块保存了ack queue,等待pipeline中的数据节点告知数据已经写入成功。
6.当客户端结束写入数据,则调用stream的close函数。此操作将所有的数据块写入pipeline中的数据节点,并等待ack queue返回成功(ack返回给客户端)。最后通知元数据节点写入完毕(通知NN)。
7.如果数据节点在写入的过程中失败,关闭pipeline,将ack queue中的数据块放入data queue的开始,当前的数据块在已经写入的数据节点中被元数据节点赋予新的标示,则错误节点重启后能够察觉其数据块是过时的,会被删除。失败的数据节点从pipeline中移除,另外的数据块则写入pipeline中的另外两个数据节点。元数据节点则被通知此数据块是复制块数不足,将来会再创建第三份备份。(逻辑就是:失败了,删除节点上的数据块,其他两个节点继续写入,通知元数据继续创建第三个备份)
补充:
由于 Hadoop 被设计运行在廉价的机器上,这意味着硬件是不可靠的,为了保证容错性,HDFS 提供了数据复制机制。HDFS 将每一个文件存储为一系列块,每个块由多个副本来保证容错,块的大小和复制因子可以自行配置(默认情况下,块大小是 128M,默认复制因子是 3)。
数据复制的实现原理
大型的 HDFS 实例在通常分布在多个机架的多台服务器上,不同机架上的两台服务器之间通过交换机进行通讯(故意存放到多个机架也是为了容灾)。在大多数情况下,同一机架中的服务器间的网络带宽大于不同机架中的服务器之间的带宽。因此 HDFS 采用机架感知副本放置策略,对于常见情况,当复制因子为 3 时,HDFS 的放置策略是:
在写入程序位于 datanode 上时,就优先将写入文件的一个副本放置在该 datanode 上,否则放在随机 datanode 上。之后在另一个远程机架上的任意一个节点上放置另一个副本,并在该机架上的另一个节点上放置最后一个副本。此策略可以减少机架间的写入流量,从而提高写入性能。
如果复制因子大于 3,则随机确定第 4 个和之后副本的放置位置,同时保持每个机架的副本数量低于上限,上限值通常为 (复制系数 - 1)/机架数量 + 2,需要注意的是不允许同一个 dataNode 上具有同一个块的多个副本。
为了最大限度地减少带宽消耗和读取延迟,HDFS 在执行读取请求时,优先读取距离读取器最近的副本。如果在与读取器节点相同的机架上存在副本,则优先选择该副本。如果 HDFS 群集跨越多个数据中心,则优先选择本地数据中心上的副本。
心跳机制和重新复制
每个 DataNode 定期向 NameNode 发送心跳消息,如果超过指定时间没有收到心跳消息,则将 DataNode 标记为死亡。NameNode 不会将任何新的 IO 请求转发给标记为死亡的 DataNode,也不会再使用这些 DataNode 上的数据。 由于数据不再可用,可能会导致某些块的复制因子小于其指定值,NameNode 会跟踪这些块,并在必要的时候进行重新复制。
数据的完整性
由于存储设备故障等原因,存储在 DataNode 上的数据块也会发生损坏。为了避免读取到已经损坏的数据而导致错误,HDFS 提供了数据完整性校验机制来保证数据的完整性,具体操作如下:
当客户端创建 HDFS 文件时,它会计算文件的每个块的 校验和,并将 校验和 存储在同一 HDFS 命名空间下的单独的隐藏文件中。当客户端检索文件内容时,它会验证从每个 DataNode 接收的数据是否与存储在关联校验和文件中的 校验和 匹配。如果匹配失败,则证明数据已经损坏,此时客户端会选择从其他 DataNode 获取该块的其他可用副本。
元数据的磁盘故障
FsImage 和 EditLog 是 HDFS 的核心数据,这些数据的意外丢失可能会导致整个 HDFS 服务不可用。为了避免这个问题,可以配置 NameNode 使其支持 FsImage 和 EditLog 多副本同步,这样 FsImage 或 EditLog 的任何改变都会引起每个副本 FsImage 和 EditLog 的同步更新。(NameNode HA)
支持快照
快照支持在特定时刻存储数据副本,在数据意外损坏时,可以通过回滚操作恢复到健康的数据状态。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。