如上一篇博客所讲,HDFS是Hadoop的一个组件。HDFS到底用来干甚么?为什么使用它?如何使用它?我将在本篇博客中详细赘述。
HDFS全称Hadoop Distributed File System,Hadoop我们已经知道了,后面的Distributed File System是个什么玩意儿?
分布式文件系统Distributed File System
这是一种文件系统的架构,允许用户在网络中的多台计算机上访问和处理存储的文件,就如同这些文件存储在本地计算机上一样。特点是有高度的可用性、可扩展性和性能。
除了HDFS,还有GFS(Google File System), EFS(Amazon Elatic File System)等等。
分布式文件系统在物理结构上是由计算机集群中的多个节点构成的,这些节点分为两类:
- 主节点/名称节点 - Master Node/ NameNode
- 从节点/数据节点 - Slave Node/ DataNode
需要注意的一点,如果你使用主节点这个叫法,那么必须配合使用从节点;反过来也是。
设计目标
分布式文件系统的设计目标主要包括透明性、并发控制、可伸缩性、容错以及安全需求等。
对于HDFS来说:
- 可以提供一定程度的访问透明性,完全支持位置透明性和伸缩透明性。——透明性。
- 访问透明性:用相同的操作访问本地资源和远程资源
- 位置透明性:不需要知道资源的物理或网络位置就能够访问它们。
- 伸缩透明性:系统和应用能够进行拓展而不改变系统结构或应用算法。 - 并发控制的机制非常简单,任何时间都只允许有一个程序在写入某个文件(也就是说上锁了)。——并发控制。
- 建立在大规模廉价机器上的分布式文件系统集群,具有很好的可伸缩性(支持节点的动态加入或退出)——可伸缩性。
- 采用多副本机制,一个文件拥有在不同位置的多个副本。并且采取故障自动检测、恢复机制。——容错性。
- 采用Java语言开发,具有很好的跨平台能力。可以在不同的操作系统和计算机上实现同样的客户端和服务器程序。——硬件和操作系统的异构性。
HDFS中的NameNode和DataNode
- NameNode负责管理分布式文件系统的命名空间,它不存储实际的数据块,但维护关于文件系统中所有文件的元数据信息。保存了两个核心的数据结构:FsImage和EditLog。
- DataNode负责数据的存储和读取,也就是数据实际存放的节点。
- FsImage: 用于存储关于整个文件系统的完整元数据快照。FsImage包含了HDFS中所有文件的详细元数据信息,包括文件的权限、结构、数据块的位置等。FsImage是HDFS核心元数据的静态表示,被用于持久化存储和系统恢复。
- EditLog: 记录所有对文件系统元数据的修改活动。
HDFS用来干甚么?
HDFS是Hadoop数据存储管理的基础。也就是说,它的作用是用来保存数据的。
为什么不能保存在本地?
如果数据集比较小或者简单的应用场景,那么保存在本地然后进行数据处理是完全可以的。但是我们之所以用Hadoop不就是因为数据量足够大、数据处理需求足够多吗?你说,啊但是我一台电脑也能达到几个T的空间啊,大不了就多加几个盘,再不行我多加一个NaS,几百个T呢。朋友,你的想法太单纯了!还记得之前博客说的吗,大数据起点单位是PB,1PB=1024TB,比T多了整整一个数量级。光空间这块,你拿什么和HDFS比?已经完败了!更不用说HDFS还有更高的容错性与可用性。一个节点数据丢失了,还有其他节点的备份。对于本地计算机来说,谁有意识将同一个数据在不同的盘里放备份?HDFS直接帮你做了这一步!(但是相对应的,如果你设置3个备份,理论上实际能存放的数据只能到整体空间的1/3)
为什么使用HDFS?
前面一个小节其实已经简单说明了原因。为了让大家更清晰HDFS的强大之处,还是要仔细说说使用HDFS的原因。
之所以选择HDFS存储数据,因为HDFS具有以下优点:
-
高容错性
数据自动保存多个副本。通过增加副本的形式提高容错性。某一个节点副本丢失之后,它可以自动恢复。数据节点会通过周期性的心跳和块报告定期和名称节点通信。- 心跳信号的间隔默认约为每3秒一次。心跳不仅表明DataNode的健康状态,也是DataNode向NameNode报告自己还在活动的机制。DataNode向上级NameNode打卡自己没有摸鱼,在正常工作。
- 如果NameNode检测到某个DataNode长时间未发送心跳,它会认为该DataNode已经失效,所有存储在失效DataNode上的数据块将被标记为需要复制,并触发复制因子(复制因子:HDFS默认将每个数据块复制3份,通常1个副本存放在本地节点,另外2个副本存放在不同的机架上的其他节点;这个复制因子是可手动配置的)修复流程,以将副本数恢复到正常水平。
- 块报告通常是每小时一次。块报告包含了该DataNode上所有HDFS数据块的详细列表。通过这些报告,NameNode可以了解整个文件系统的块位置和存储信息。
- 块报告包括每个数据块的块ID和块元数据,如版本信息和时间戳等。这些信息帮助NameNode维护整个HDFS的元数据,并确保数据分布的一致性和正确性。
- NameNode使用收到的块报告来更新其全局视图,确保数据一致性。如果发现某个数据块的副本数低于预设的复制因子,NameNode将指示其他DataNode创建更多副本,以满足冗余要求。
-
适合批处理
移动计算而不是移动数据。与其将大量数据传输到计算资源所在的位置,不如将计算任务发送到存储数据的节点上去执行。这样做的好处是可以显著减少网络带宽的消耗,提高整体处理效率,特别是在处理大规模数据集时。- 在实际的Hadoop集群中,NameNode在调度MapReduce等作业时,会尽可能地选择存有所需数据块的DataNode或与该DataNode网络距离较近的节点来执行任务。这样做可以最大限度地利用数据本地性,减少计算过程中数据移动的需要。
-
适合大数据处理
前面已经说了,因为它是搭建在一个计算机集群当中的。能处理的数据大小,主要取决于集群空间大小,当然,为了处理某个数据而扩充集群也是可以的。 -
流式文件访问
一次写入,多次读取。文件一旦写入不能修改,只能追加。这样可以保证数据的一致性。- 如果你需要修改一个已经上传的文件,最直接的方法是在本地进行修改,然后将修改后的文件重新上传到HDFS,覆盖原有文件或作为一个新文件保存。
- 虽然HDFS默认不允许修改文件中的内容,但它允许在现有文件的末尾追加数据。如果你的需求仅限于向文件末尾添加数据,可以利用这一特性。
- 在HDFS中,对文件的任何更新或追加操作都会自动应用到该文件的所有副本。也就是说,如果你上传了一个修改过的文件覆盖原文件,或者在原文件上进行了追加操作,HDFS将自动更新所有的副本,以保证数据的一致性和可靠性。
-
可构建在廉价机器上
便宜啊,实惠啊。
HDFS缺点
提到了优点,也必须得说一下缺点。毕竟不能一味地吹嘘某个东西很好,要客观看待事物。
- 不适合低延时数据访问。
因为hdfs被设计为支持大数据处理,特别优化了高吞吐量的数据访问,而不是低延时。这意味着它非常不适合于快速的随机访问。并且因为副本机制,任何对文件的读写操作都可能涉及跨多个节点的网络通信。尤其是写操作,需要将数据块同时写入多个节点,确保所有副本都同步更新,这增加了操作的延时。 - 不适合小文件存储。
- 每个文件和每个数据块在HDFS中都有相应的元数据(如权限信息、块的位置等),这些元数据存储在NameNode的内存中。如果有大量的小文件,每个文件和它的数据块都需要相应的元数据,这会消耗大量的内存资源。对于NameNode而言,内存是一个宝贵的资源,过多的小文件将极大地增加NameNode的内存压力,降低系统的效率。
- 并且,在HDFS中,文件默认被分割成至少一个数据块(默认大小为128MB或256MB)。对于小于块大小的文件,它们不会占满一个完整的数据块,但每个文件仍然至少被存储为一个数据块。如果有大量小文件,会造成大量的数据块未被充分利用,这种空间的浪费会导致存储效率低下。
- 对于1MB的文件存储在HDFS中,默认块大小为128MB的情况下,该文件只会占用1MB的实际存储空间,并不会占满整个128MB的数据块。每个块的大小定义了该块可以占用的最大空间,但实际使用的空间取决于文件的实际大小。
这意味着,尽管块的容量是128MB,但如果文件只有1MB,那么只有1MB的空间被使用,剩余的127MB空间在该块中仍然可用,但不能用于存储其他文件的数据。这是因为在HDFS中,一个文件的数据块不会与另一个文件共享。因此,对于包含许多小文件的系统来说,这种方式可能导致存储效率低下,因为大量的存储空间没有被充分利用。
- 不允许文件随机修改
这也是前面提到的,因为要保证数据的一致性。
HDFS写入数据流程
这是一个涉及客户端、NameNode和DataNodes之间交互的过程。具体的步骤如下:
步骤1: 客户端请求
- 开始写入:当用户或应用程序决定写入数据到HDFS时,客户端发起一个写操作请求。这通常通过使用Hadoop的API,如在Java程序中使用FileSystem.create()方法或通过Hadoop的命令行工具。
步骤2: 与NameNode交互
- 获取权限和位置:客户端首先与NameNode通信以获取写数据的权限。如果用户有权写入数据,NameNode会进行下一步。
- 查找DataNode:NameNode选择一组DataNode来存储将要写入的数据块。选择标准基于DataNode的可用性、存储空间和距离客户端的近程原则。
步骤3: 写数据到DataNodes
- 数据流管道:NameNode回复客户端,提供一组可以写入数据块的DataNodes的地址。客户端直接与这些DataNode建立连接,并按照NameNode指定的顺序,形成一个“管道”。
- 数据块写入:客户端将数据分割成多个块(默认大小如128MB或256MB),然后按顺序发送到管道中的第一个DataNode。每个DataNode接收到块数据后,存储该数据块的副本,并将数据块转发给管道中的下一个DataNode。这个过程一直持续到所有选定的DataNodes都接收到数据块。
步骤4: 确认和关闭
- 写入确认:每个DataNode写入数据后会向客户端发送确认。一旦所有的DataNodes都成功写入它们的数据块并确认,客户端完成这个数据块的写操作。
- 完成操作:当所有数据块都被写入后,客户端通知NameNode写操作已经完成,NameNode随后更新元数据,包括新写入文件的数据块列表和它们的位置。
如何使用HDFS?
两种方式。一种是客户端直接通过cmd和hdfs取得联系。两外一种是通过Java API,编写Java代码来和hdfs取得联系。
shell命令和Linux命令是基本一致的,需要在命令前面加一个‘-’,并且写出是hdfs