1. 什么是 Hadoop?
Hadoop 是一个开源的分布式系统基础架构,用于存储和处理大规模数据集。它主要包含 HDFS(Hadoop Distributed File System)分布式文件系统、MapReduce 分布式计算框架以及 YARN(Yet Another Resource Negotiator)资源管理器。HDFS 负责数据的分布式存储,将大文件分割成多个数据块存储在不同节点上;MapReduce 用于分布式并行处理数据;YARN 则负责集群资源的管理和调度,为不同的应用程序分配计算资源。
2. 解释 MapReduce 编程模型及其主要组成部分
MapReduce 是一种分布式计算编程模型,用于大规模数据集的并行处理。它主要由 Map 和 Reduce 两个阶段组成,并且中间会有 Shuffle 过程来进行数据的重组和分发。
- Map 阶段:Map 函数以键值对(key - value)作为输入,对输入数据进行处理,将其转换为一组新的键值对输出。例如,在统计单词出现次数的场景中,输入数据可能是文本行,Map 函数会将每一行文本按单词分割,然后将每个单词作为键,值设为 1 输出,即把输入的文本数据转换为单词和出现次数(初始为 1)的键值对。
- Shuffle 阶段:该阶段负责将 Map 阶段输出的键值对按照键进行分组和排序。它会将相同键的值收集到一起,发送到对应的 Reduce 节点进行处理。例如,所有单词 “hello” 对应的键值对会被收集到一起,发送到同一个 Reduce 节点。
- Reduce 阶段:Reduce 函数以键和对应的值列表作为输入,对相同键的值进行合并处理。在单词统计场景中,Reduce 函数会将所有单词 “hello” 对应的值(即出现次数 1)进行累加,得到 “hello” 这个单词在整个数据集中的总出现次数。
3. split 机制
spilit 是在mr 处理的map端之前产生的概念,split切片大小,默认等于block*1.1,在FileInputFormat中计算切片大小的逻辑:
blocksize:默认是 128M,可通过 dfs.blocksize 修改
minSize:默认是 1,可通过 mapreduce.input.fileinputformat.split.minsize 修改
maxsize:默认是 Long.MaxValue,可通过 mapreduce.input.fileinputformat.split.maxsize 修改
Hadoop FileInputFormat 源码:
public static final String SPLIT_MAXSIZE = "mapreduce.input.fileinputformat.split.maxsize";
public static final String SPLIT_MINSIZE = "mapreduce.input.fileinputformat.split.minsize";
protected long computeSplitSize(long blockSize, long minSize, long maxSize) {
return Math.max(minSize, Math.min(maxSize, blockSize));
}
为什么split不是与block 一一对应的?
大量小文件场景,map进程造成资源严重浪费。
针对大小文件场景可以手动配置。
4. namenode,datanode,secondaryNameNode分别是干什么的?
namenode,在基于主从架构的hdfs文件系统中是主节点,其主要职责就是对hdfs中的文件的元信息,副本数,文件目录树,block 数据节点信息;
datanode,它是从节点也是数据节点,基于本地磁盘存储 block(文件的形式),有相关数据块的长度、效验和、时间戳,与namnode保持心跳,汇报 block 状态。
secondaryNameNode,检查点节点,namenode 日志高可用的关键,其主要作用就是将namenode的元数据日志信息合并后备份,防止元数据丢失。
元信息:是数据文件的block大小,文件副本存储位置,副本数量,block 数量,主要体现在edits文件和fsimage文件。
副本数:hdfs 中同一个文件在多个节点中所存储的总数量,也是实现持久化和保证安全性的关键。
文件目录树:hdfs提供了一个可以维护的文件目录,该文件目录下存储着有关所有hdfs的文件。
block 数据节点信息:如a文件在01和02节点中存储,该信息称为数据节点信息。
5. HDFS 的架构及其工作原理
HDFS 采用主从架构,主要由 NameNode 和 DataNode 组成。
- NameNode:作为主节点,管理文件系统的命名空间,存储文件的元数据信息,如文件名、文件权限、文件到数据块的映射关系以及数据块到 DataNode 的存储位置等。它还负责处理客户端的文件操作请求,如文件创建、删除、重命名等。
- DataNode:作为从节点,负责实际的数据存储。它以数据块(block)的形式存储数据,并定期向 NameNode 汇报自己所存储的数据块信息。
工作原理方面,当客户端进行写操作时,首先向 NameNode 请求上传文件,NameNode 检查目标文件是否已存在以及父目录是否存在,确认可以上传后,返回可用的 DataNode 节点列表。客户端将文件数据按块写入第一个 DataNode,第一个 DataNode 会将数据块同步复制到列表中的其他 DataNode,形成数据的冗余备份。在读取数据时,客户端向 NameNode 请求下载文件,NameNode 根据元数据找到文件对应的数据块所在的 DataNode 地址,客户端从这些 DataNode 读取数据块并组装成完整的文件。
6. HDFS 如何保证数据的高可用性和容错性?
- 数据冗余存储:HDFS 默认将每个数据块复制 3 份(可配置),存储在不同的 DataNode 上。当某个 DataNode 出现故障时,其他副本仍然可用,确保数据不会丢失。
- 心跳机制:DataNode 周期性地向 NameNode 发送心跳消息,汇报自身状态。如果 NameNode 在一定时间内未收到某个 DataNode 的心跳,就会认为该 DataNode 出现故障,从而将其管理的数据块复制到其他正常的 DataNode 上,以保证数据的可用性。
- 元数据备份:NameNode 的元数据信息会定期进行备份,Secondary NameNode(辅助 NameNode)会协助 NameNode 进行元数据的检查点操作,将内存中的元数据信息定期合并到磁盘上的编辑日志中,防止元数据丢失。在 NameNode 发生故障时,可以使用备份的元数据进行恢复。
7. HDFS 的读写流程是怎样的?
读流程
客户端通过 DistributedFileSystem 向 NameNode 发起读取文件的请求。
NameNode 根据元数据信息,查找文件对应的所有数据块的存储位置,返回数据块所在的 DataNode 列表。
客户端从返回的 DataNode 列表中选择一个 DataNode(通常采用就近原则,优先选择网络距离近的节点),向其发送读取数据块的请求。
被选中的 DataNode 开始从本地磁盘读取数据块,将数据以 Packet(数据包)为单位传输给客户端。客户端接收 Packet 并进行校验,在校验无误后,将数据组装成完整的数据块。
客户端按照文件的数据块顺序,依次从各个 DataNode 读取所有数据块,最终组装成完整的文件。
写流程
客户端通过 DistributedFileSystem 向 NameNode 发起上传文件的请求。
NameNode 检查目标文件是否已存在,以及父目录是否存在。若目标文件不存在且父目录存在,则允许上传,否则返回错误信息。
NameNode 根据数据块副本策略,返回一组 DataNode 节点列表,用于存储文件的数据块副本。
客户端与第一个 DataNode 建立连接,开始上传数据。客户端将数据按 Packet 写入第一个 DataNode,第一个 DataNode 在接收到 Packet 后,会将其转发给第二个 DataNode,第二个 DataNode 再转发给第三个 DataNode(假设副本数为 3),形成数据的流水线复制。
每个 DataNode 在接收到 Packet 后,会进行校验和存储操作,并向客户端发送确认消息。当客户端接收到所有副本 DataNode 的确认消息后,认为该数据块上传成功。
客户端继续上传下一个数据块,重复上述步骤,直到整个文件上传完成。
8. 什么是 Secondary NameNode?
Secondary NameNode 并非 NameNode 的热备节点,它的主要作用是协助 NameNode 进行元数据的管理和恢复。它会定期与 NameNode 进行通信,从 NameNode 获取命名空间镜像文件(fsimage)和编辑日志文件(edits)。然后,Secondary NameNode 将 fsimage 加载到内存中,并将 edits 中的操作应用到 fsimage 上,生成一个新的合并后的 fsimage 文件。这个新的 fsimage 文件会被传输回 NameNode,NameNode 用它替换旧的 fsimage 文件,并将编辑日志文件清空。通过这种方式,Secondary NameNode 可以减少 NameNode 在进行元数据检查点操作时的负担,加快 NameNode 在故障恢复时的速度,因为恢复时可以直接使用合并后的 fsimage 文件,而不需要重新应用大量的编辑日志操作。
9. 如何处理 NameNode 的单点故障问题?
为了解决 NameNode 的单点故障问题,主要采用以下几种方案:
- NameNode HA(High Availability)高可用方案:配置两个 NameNode,一个处于 Active 状态,负责处理客户端的请求和管理元数据;另一个处于 Standby 状态,实时同步 Active NameNode 的元数据信息。当 Active NameNode 出现故障时,Standby NameNode 可以快速切换为 Active 状态,继续提供服务,保证系统的高可用性。通常会借助 ZooKeeper 来实现 NameNode 的状态管理和故障切换。
- 使用 NFS(Network File System)共享存储:将 NameNode 的元数据存储在共享的 NFS 存储设备上,这样即使 NameNode 节点出现故障,新启动的 NameNode 也可以从共享存储中获取元数据,从而实现快速恢复。不过,这种方案对 NFS 的可靠性有较高要求。
- 定期备份元数据:通过定期备份 NameNode 的元数据,如利用 Secondary NameNode 进行元数据合并和备份,在 NameNode 故障时,可以使用备份的元数据进行恢复,但这种方式可能会导致一定时间内的数据丢失,因为备份是周期性进行的。
10. 什么是 Hadoop 的块(Block)?为什么要使用块?
Hadoop 的块是 HDFS 中数据存储的基本单位。一个文件在 HDFS 中会被分割成多个块进行存储,每个块的大小可以通过配置参数设置,默认大小在 Hadoop 2.x 版本中通常为 128MB。
使用块的主要原因如下:
- 简化存储管理:将大文件分割成固定大小的块进行存储,便于文件系统对数据的管理和操作。例如,在进行数据读写时,可以以块为单位进行,提高数据处理的效率。
- 提高可靠性:每个块可以在多个 DataNode 上进行冗余存储,当某个 DataNode 出现故障时,其他副本上的块仍然可用,保证数据的可靠性。
- 支持并行处理:MapReduce 计算框架可以并行处理不同的数据块,提高数据处理的并行度和速度。不同的 Map 任务可以同时处理不同的数据块,从而实现对大规模数据的快速处理。
11. HDFS 中的数据块大小可以配置吗?如果可以,如何配置?
HDFS 中的数据块大小是可以配置的。在 Hadoop 的配置文件hdfs-site.xml中,可以通过设置dfs.blocksize属性来指定数据块的大小。例如,要将数据块大小设置为 256MB,可以在hdfs-site.xml中添加如下配置:
<property>
<name>dfs.blocksize</name>
<value>268435456</value> <!-- 256MB,以字节为单位,256 * 1024 * 1024 = 268435456 -->
</property>
修改完配置文件后,需要重启 Hadoop 集群的 NameNode 和 DataNode 服务,使配置生效。需要注意的是,合理设置数据块大小非常重要,过大或过小的块大小都可能影响系统的性能。块设置过大,从磁盘传输数据的时间会明显大于寻址时间,导致程序在处理这块数据时变得非常慢;块设置过小,存放大量小文件会占用 NameNode 中大量内存来存储元数据,而且文件块过小,寻址时间增大,导致程序一直在找 block 的开始位置。