大数据修炼之路(二):HDFS 核心架构与面试通关指南
引言:什么是大数据思维?
在进入 HDFS 之前,先建立一个核心思维:分而治之(Divide and Conquer) 。
当你面对 1TB 的数据,单机内存(1GB)根本装不下时,不要尝试搞什么复杂的算法。直接用最朴素的思想:* 查重 :根据 HashCode 对 1000 取模,把大文件拆成 1000 个小文件,保证相同的数据落在一个文件,再逐个用 HashSet 查重。* 排序 :切分成多个小文件,内部排好序,最后采用归并排序 合并。
这就是 HDFS 底层最朴素的生存逻辑。
一、 HDFS 核心架构:管账的不管钱
HDFS(Hadoop Distributed File System)是一个主从(Master/Slave)架构 的分布式文件系统。它的核心理念极其精妙:控制流与数据流彻底分离。
| 角色 | 定位 | 核心职责 | 面试必背金句 |
|---|---|---|---|
| NameNode | 大脑(管账) | 管理命名空间、维护元数据(目录树)、配置副本策略(默认3份)、处理客户端读写请求。绝对不存真实数据! | "管账的不管钱" |
| DataNode | 苦力(管钱) | 存储实际的数据块(Block),执行读写操作。每3秒向 NameNode 发送心跳(Heartbeat),每6小时汇报一次块信息(Block Report)。 | "干活的不管账" |
| Client | 跑腿 | 切分文件、向 NameNode 请求元数据、直接与 DataNode 建立 Pipeline 传输数据。 | "切块的搬运工" |
| Standby NameNode | 备用大脑 | 辅助 Active NameNode 合并 Fsimage 和 Edits,并在故障时无缝接管。 | "随时待命的替补" |
💡 核心概念:Block(数据块)
- 默认大小:Hadoop 1.x 为 64MB,Hadoop 2.x/3.x 为 128MB。
- 为什么这么大?为了减少寻址开销(大卡车拉砖比三轮车拉砖效率高)。
- 优势:方便管理、支持存大文件、容错性高、简化存储机制。
二、 高可用(HA)与联邦(Federation):解决单点故障与内存瓶颈
1. HA(High Availability)架构
单点 NameNode 挂掉,整个集群瘫痪。HA 架构引入了 Active/Standby 双 NameNode。
- 元数据同步 :依靠 QJM(Quorum Journal Manager)。Active NameNode 把 EditLog 写入 JournalNode 集群(奇数节点,基于 Paxos,保证数据完整)。Standby 定期去 QJM 同步。
- 自动故障转移 :依靠 ZKFC(ZooKeeper Failover Controller) 和 ZooKeeper 。
- 两个 ZKFC 争抢 ZooKeeper 上的临时节点,抢到的成为 Active。
- 如果 Active 的 ZKFC 挂了,ZooKeeper 临时节点消失,Standby 的 ZKFC 监听到事件,把备节点提升为 Active。
- 防脑裂(Brain Split) :引入 隔离机制(Fencing) 。若旧 Active 假死,新 Active 会通过 SSH 把旧节点降级或直接 Kill(
sshfence),确保任何时刻只有一个 NameNode 对外服务。
2. Federation(联邦)
HA 仍然受限于单 NameNode 的内存上限(元数据全在内存里)。
Federation 引入多个独立的 NameNode ,每个负责一部分命名空间(Namespace Volume),它们共用底层的 DataNode 存储,Block Pool 独立。这在 AI 海量数据场景下,是水平扩展元数据能力的最佳实践。
三、 读写数据流程:面试重灾区(必画图)
1. HDFS 写数据流程(Pipeline 流水线)
- 客户端请求 NameNode 上传文件。
- NameNode 检查权限、目录树,返回可用的 3 个 DataNode 地址(机架感知策略:本地机架1个,不同机架1个,同机架另1个)。
- 客户端向 DN1 请求建立通道,DN1 向 DN2,DN2 向 DN3 建立通道(Pipeline 管道)。
- 客户端以 Packet(默认 64KB) 为单位,先发给 DN1,DN1 边收边传给 DN2,DN2 传给 DN3。
- DN3 收到后,返回 ACK 给 DN2,DN2 给 DN1,DN1 给客户端(确认队列 AckQueue)。
- 当所有 Packet 传完,关闭流,NameNode 更新元数据。
2. HDFS 读数据流程
- 客户端请求 NameNode 读取文件。
- NameNode 返回文件所有 Block 所在的 DataNode 地址(按网络拓扑距离排序,最近的靠前)。
- 客户端直接连接最近的 DataNode 读取数据。
- 每读完一个 Block,进行 Checksum 校验。若出错,通知 NameNode,从下一个副本节点重试。
3. 机架感知策略(Rack Awareness)
在同一个机架内的数据传输速度远大于跨机架。副本存放策略极为关键:
- 第一个副本:如果客户端在集群内,放在客户端同节点;如果在集群外,选资源丰富的节点。
- 第二个副本:放在不同机架的节点(防止整个机架断电)。
- 第三个副本:放在与第二个副本同机架的其他节点(兼顾性能)。
四、 Hadoop 3.x 新特性(面试加分项)
- 纠删码(Erasure Coding, EC)
- 痛点:3 副本存储利用率只有 1/3,成本极高。
- 解法 :使用
RS(k, m)算法(例如 RS-6-3),6 个数据块 + 3 个校验块。存储开销从 3 倍降到 1.5 倍。 - 代价 :数据恢复需要解码运算,所以适合存储冷数据(不经常访问的历史归档数据)。
- 多 Standby NameNode:支持 1 个 Active + 多个 Standby,容忍更多节点故障。
- DataNode 内部磁盘均衡 :新增
hdfs diskbalancer命令,解决单机多磁盘之间数据倾斜问题。 - 端口变更 :NameNode HTTP UI 端口从 50070 变为 9870(面试别记错)。
五、 总结
"HDFS 本质上是一个'一次写入、多次读取'的分布式文件系统。它的核心架构是主从结构,NameNode 只负责管理元数据,不存真实数据;DataNode 存数据块(默认 128MB),默认 3 副本。
它的读写流程最核心的理念是**'数据流与控制流分离'**。客户端只向 NameNode 要元数据,然后直接与 DataNode 建立 Pipeline 管道传输数据,避免了 NameNode 成为性能瓶颈。
为了解决 NameNode 单点故障,我们使用 ZooKeeper + ZKFC + QJM 搭建了 HA 架构,并通过 fencing 机制防止脑裂。在 Hadoop 3.x 中,还可以用纠删码(EC)来降低存储成本,用 Federation 来扩展元数据的能力。"
面试题
面试官问:"你了解 HDFS 的 HA 架构吗?
"HDFS HA 的核心是为了解决单点故障。它采用了Active/Standby 双 NameNode 架构,并依赖 QJM(Quorum Journal Manager) 和 ZooKeeper 来实现。
第一,在数据同步上,Active NameNode 把 EditLog 写入 JournalNode 集群,采用过半写成功机制(Paxos)。Standby 从 QJM 实时拉取日志,保证元数据与主节点基本一致,实现热备。
第二,在故障转移上,每个 NameNode 都有一个 ZKFC 进程。ZKFC 通过在 ZooKeeper 上抢临时节点来决定谁是 Active。一旦 Active 宕机,ZooKeeper 临时节点消失,Standby 的 ZKFC 就会抢到锁,把自己升级为 Active。
第三,为了防止脑裂,HA 引入了 Fencing 隔离机制。新主转正前,会通过 SSH(sshfence)强制 Kill 掉旧主的进程,确保集群永远只有一个 Active 节点,保证数据的一致性。"
面试官问:说下HDFS 的读取流程和写入流程
"HDFS 读写流程的核心原则是数据流与控制流分离,NameNode 只负责管元数据,不参与真实数据的搬运,客户端直接和 DataNode 进行海量数据的传输。
先说写入流程。客户端先向 NameNode 发起上传请求,NameNode 检查权限和目录后,根据机架感知策略返回三个可用的 DataNode 地址。接着客户端向第一个 DataNode 请求建立连接,第一个找第二个,第二个找第三个,建立一条 Pipeline 流水线管道。然后客户端把文件切分成 128MB 的 Block,再把 Block 切成 64KB 的 Packet,只发给第一个 DataNode,它边收边传给下一个。第三个收到后返回 ACK,层层回传,客户端收到所有 ACK 才算这个 Block 传完,再向 NameNode 申请下一个 Block。全部传完后,NameNode 才正式提交元数据。如果传输中途某个 DataNode 挂了,管道会重建,剔除坏节点,保证数据不丢。
接着是读取流程。客户端向 NameNode 请求读取文件,NameNode 返回该文件所有 Block 的 DataNode 地址列表,这个列表是按网络拓扑距离排好序的,离客户端最近的排最前面。然后客户端直接连接最近的 DataNode 读取数据。每读完一个 Block,都要进行 Checksum 校验和验证,如果发现数据损坏或节点超时,立刻去下一个副本节点重读,读完后在本地合并成完整文件。
所以整个流程里,NameNode 只负责指路和记账,真正搬运数据的是客户端和 DataNode 直接交互,这也是 HDFS 能支撑海量数据高吞吐的关键。"