hadoop是什么
hadoop是一个开源的大数据框架同时也是一个分布式计算的解决方案
hadoop = HDFS(分布式文件系统) + MapReduce(分布式计算)
HDFS 概念
数据块
NameNode
- Secondary NameNode
DataNode
数据块:
数据块是一个抽象的块,不是整个文件。
块大小的计算
块大小取决于dfs.blocksize,hadoop 1.x默认块大小为64M,hadoop 2.x之后默认块大小是128M。
默认是128M的原因,基于最佳传输损耗理论。
最佳传输损耗理论:在一次传输中,寻址时间占用传输时间的1%时,本次传输的损耗最小,为最佳性价比传输。
目前硬件的发展条件,普通磁盘写的速率大概为100M/s,寻址时间一般为10ms,下面可以对128M的由来进行推演。
10ms / 1% = 1s
1s * 100M/s = 100M
但计算到这儿,还要考虑块传输时的校验问题,每传输64KB校验一次,所以块大小必须为2的n次方,而最接近100的2的n次方为128,所以块大小为128M。
如果公司使用的是固态硬盘,写的速度是300M/s,将块大小调整到256M
如果公司使用的是固态硬盘,写的速度是500M/s,将块大小调整到512M
块大小需要合适调节
不能太大
比如当前有文件a,1G大小
128M一块,存8块 和 1G一块,存一块 相比较
场景1:下载一个128M大小的文件
场景2:在上传文件时,断网了
缺点:
- 在一些分块读取的场景,不够灵活,会带来额外的网络消耗。,如场景1,明明只需要128M的数据,分成1G一块的却需要对个文件大小进行下载
- 在上传文件时,一旦发生故障,会造成资源的浪费。如场景2,假设我要上传一个1G大小的文件,分成128M一块就可以有效防止断网时前面数据的丢失,而分成1G一块,在上传到1023M时,一旦断网数据将会全部丢失
不能太小
比如当前有文件a,128M大小
128M一块,存8块 和 1M一块,存128块 相比较
缺点:
- 块太小,同样大小的文件,会占用过多的NN的元数据空间
- 块太小,在进行读写操作时,会消耗额外的寻址时间
对块进行抽象的好处
一个文件的大小可以大于集群网络中任一个磁盘大小。
因为可以对文件进行分块存储,所以在极端情况下,一个集群只存放了一个文件,该文件占满了集群中的所有磁盘
使用抽象块而不是整个文件作为存储单元,可以简化存储子系统的设计。首先块的大小是固定的,所以一个磁盘能存出多少个块很容易就能计算出来。另外也消除了对于元数据的顾虑,块只是要存储的大块数据,而文件的元数据,列入权限信息等等,并不需要与块进行一同存储,可以单独进行管理
块适合于提供备份和冗余容错的作用,通过将块进行复制副本,通常是3个,当因损坏或者机器故障而丢失的块,我们便可以从其他候选机器将副本块复制到另一台能够正常工作的机器上,保证副本的数量保持不变
HDFS集群有两个节点,以管理节点 —— 工作节点的模式运行着,分别是 NameNode 和 DataNode
NameNode
NN的作用
- NN保存HDFS上所有文件的元数据
- NN负责接受和处理客户端的请求
- NN负责接受DN上报的信息,给DN分配任务(维护副本数)
- 和DN保持心跳,向DN下达命令
元数据的存储
- 元数据存储在fsiamge文件+edits文件中
- fsimage(元数据的快照文件)
- edits(记录所有写操作的文件)
存储的元数据分为两种
- inodes : 记录文件的属性和文件由哪些块组成,记录到edits和fsimage文件中
- 块的位置映射信息: 由NN启动后,接收DN的上报,动态生成
联邦HDFS
NameNode节点在内存中保存着文件系统中每个文件和每个数据块的引用关系,所以NameNode的内存会成为集群扩展的一个瓶颈
在Hadoop 2.x版本中引入了联邦HDFS,允许在集群中添加多个NameNode节点,以实现扩展
在联邦环境下,每个NameNode都维护着一个命名空间卷( 比如NameNode_1负责 /usr,NameNode_2 负责 /share ),由命名空间的元数据和数据块池组成。
- 数据块池里面存放着该命名空间下所有的数据块
命名空间卷之间不进行通信,甚至其中一个挂掉也不会影响另一个。但每个DataNode需要注册到每一个NameNode上,也需要存储着来自各个数据块池的数据块
DataNode
存储以及检索数据块
向NameNode更新所存储块的列表
NameNode容错
诺NameNode失效,我们将无法访问到文件系统上的所有文件。因为我们不知道怎么去根据DataNode的块去重建文件。因此需要对NameNode进行容错处理
两种容错机制:
对于组成文件系统元数据持久状态的文件 我们可以使NameNode在多个文件系统上对其进行保存,比如最常用的就是在将持久状态写入本地磁盘的同时也将其写入到远程挂载的NFS网络文件系统中。
- 运行一个辅助NameNode节点 (Secondary NameNode)该节点却不能被用作NameNode,它的主要作用是定期合并编辑日志文件和命名空间镜像文件,防止其过大。它在合并后会生成命名空间镜像文件的副本,当NameNode失效时会启用。但是辅助NameNode节点保存的信息总是会滞后于NameNode节点,所以如果想要实现容错机制,可以在主NameNode节点失效后,将保存在NFS的文件系统元数据复制到Secondary NameNode上来,将其作为新的主NameNode运行。
“ HA高可用 ”
HDFS 优点
适合大文件存储,支持TB、PB文件的存储
可以构建在廉价的机器上,并能够提供容错机制和恢复机制
支持流式数据访问,一次写入,多次读取更高效
HDFS 缺点
不适合大量小文件存储
不适合并发写入,也不支持文件随机修改(在线编辑)
不支持随机读等低延迟的访问方式
HDFS读写文件的原理解析
详细的读流程图如下所示:
客户端如何读取DataNode上所存的块数据步骤:
客户端通过FileSystem对象的open()方法去打开希望读取的文件
此时,DistributedFileSystem会通过RPC去调用NameNode(RPC提供了一组与机器、操作系统以及低层传送协议无关的存取远程文件的操作),然后NameNode会返回存有block的所有的DataNode信息,(对于每个块,NameNode会返回存有该副本的DataNode信息,并按照距离对DataNode进行排序)
然后,DistributedFileSystem类会返回一个FSDataInputStream对象给客户端去读取数据
- 该FSDataInputStream类会封装DFSInputStream对象,该对象管理着datanode和namenode的I/O
客户端对这个数据流调用read()方法去读取块数据
- DFSInputStream里存放着文件前几个块的副本的所有的DataNode地址,于是会去连接距离最近的DataNode
Client通过对数据流反复调用read()方法,在DFSInputStream读取完第一个块数据以后,会关闭与该DataNode的连接,转而去连下一个距离最近且存着第二块数据的DataNode节点,继续读取数据
Cilent在读取了文件前几个块的数据后,根据需要,Client可能会询问NameNode节点检索下一批DataNode数据块的位置,然后继续通过DFSInputStream去读取数据。客户端一旦完成了数据读取,便会对FSDataInputSream调用close()方法
读取时的细节
在读取过程中,如果某一个DataNode发送故障,DFSInputStream会尝试连接另外一个临近的DataNode,并记下该故障的DataNode,保证后面不会再去该节点读取数据。DFSInputStream会通过校验和检查读取文件的正确性
这种读取流程,让NameNode的工作量大大滴减少,只需要响应客户端的块位置请求即可,无需响应数据请求,所以可以支持高扩展
距离是按照带宽来计算的,一般来说可以依据场景,对带宽进行递减:
- 同一节点的不同进程
- 同一机架的不同节点
- 同一数据中心的不同机架上的节点
- 不同数据中心的节点
详细的写流程图如下所示:
客户端如何将文件写入到DataNode的步骤:
create:
客户端通过FileSystem对象的create()方法去创建希望的文件
客户端通过DistributedFileSystem对象调用create()方法请求NameNode创建一个新文件
DistributedFileSystem的create()方法会返回一个FSDataOutputStream对象给客户端
FSDataOutputStream封装了一个DFSOutputStream对象,在其构造函数中会使用RPC远程调用NameNode的create()方法来创建一个文件
NameNode会先对创建的文件进行检查(比如该客户端有没有权限创建文件,该文件名是否已经存在?),诺检查没有问题,NameNode则为创建新文件做一条记录,否则创建失败,并向客户端返回IOException异常
write packet 和 ack packet
客户端写入数据块时,DFSOutputStream将其分成一个一个的包(packet),将packet放到pipeline(管线)里进行写入。写入时,会使用两个队列,一个是 “数据队列”,用于存放要写入的packet;一个则是 “确认队列”,用于接收DataNode发来的确认Ack
写入过程:(DataStreamer负责处理)
一般会使用FSDataOutputStream的write方法
FSDataOutputStream的write方法会调用DFSOutputStream的write方法,而DFSOutputStream继承自FSOutputSummer,所以实际上是调用FSOutputSummer的write方法
首先将package 1写入DataNode 1然后由DataNode 1负责将package 1写入DataNode 2,同时客户端可以将pacage 2写入DataNode 1
然后DataNode 2负责将package 1写入DataNode 3, 同时客户端可以讲package 3写入DataNode 1,DataNode 1将package 2写入DataNode 2
就这样将一个个package排着队的传递下去,直到所有的数据全部写入并复制完毕
确认过程:
DataStreamer线程负责把准备好的数据packet,顺序写入到DataNode,未确认写入成功的packet则移动到ackQueue,等待确认。
DataStreamer线程传输数据到DataNode时,要向namenode申请数据块,在NameNode分配了DataNode和block以后,createBlockOutputStream开始写入数据。
只有对于一个数据包(packet)收到管道内的所有DataNode的ack之后,才能将该数据包从确认队列中删除。
当写入时有DataNode发生故障,导致数据无法正常写入,该怎么处理??
DataStreamer会启动ResponseProcessor线程,它负责接收datanode的ack
- 首先将管线关闭
- 将确认队列的数据包添加回数据队列的前端
- 将发生故障的DataNode从管线中移除
- 在另一个正常的DataNode节点对当前的数据块做一个标记,并将标识发给NameNode,使损坏的DataNode恢复正常后能够删除已存储的部分数据块
- 通过RPC调用DataNode的recoverBlock方法来恢复数据块
- 以剩下的DataNode节点建立新的管线,继续写入数据(NameNode注意到块复本数量不足时,会重新添加一个DataNode进行副本数据保存)
HDFS如何选择副本的存储位置?(机架感知)
默认的机架感知策略:
- 第一个副本一般放在客户端,如果客户端在数据中心之外,则会随机选择数据中心的一个节点
- 第二个副本保存在和第一个不同机架的一个节点
- 第三个副本则选择和第二个副本在一个机架上,但是是不同的节点
- 后面的副本会随机选择,不过系统会尽量避免一个机架上存放多个副本