订阅更新

一个文件上传后被切成了3份——HDFS的Block、副本和存储原理

Hadoop手册 第 2 题 约 7 分钟读完

本文目录
  1. 先说结论
  2. Block机制——为什么切成128MB?
  3. 3副本放置策略——机架感知
  4. HDFS写入流程——Pipeline写入
  5. HDFS读取流程——就近读取
  6. 实战经验:Block大小对Spark作业的影响
  7. 面试高频追问
  8. 总结

先说结论

HDFS把文件切分成固定大小的Block(默认128MB),每个Block存3个副本在不同的DataNode上。切分的目的是并行处理和容错:大文件切成小块后可以分布在多台机器上同时读写;3副本保证任何单机故障都不丢数据。

面试核心三个点:① Block的大小为什么是128MB(不是更大或更小);② 3副本的放置策略(机架感知);③ 读写数据的完整流程。

先把这段讲顺;面试官追问时,再展开下面的原理和细节。

详细讲解

HDFS的Block和副本机制是分布式存储的基石,也是面试的必考题。面试官会问:Block为什么是128MB?3副本怎么放置?读写数据的流程是怎样的?这些问题看起来基础,但很多人只背了结论不理解原因。在某短视频平台处理日均12亿行埋点数据时,数据就存在S3上(S3的底层原理和HDFS类似),理解Block机制帮我优化了很多存储和查询的性能问题。

Block机制——为什么切成128MB?

# 一个300MB的文件上传到HDFS后
#
# 被切成3个Block:
# Block-1: 128MB (存在DN-1, DN-3, DN-5)
# Block-2: 128MB (存在DN-2, DN-4, DN-6)
# Block-3: 44MB  (存在DN-1, DN-4, DN-3)
#
# 每个Block有3个副本,分布在不同的DataNode上
# NameNode记录了每个Block在哪些DataNode上
#
# Block大小的演进:
# Hadoop 1.x: 64MB(当时磁盘和网络较慢)
# Hadoop 2.x+: 128MB(磁盘和网络提速,更大的Block减少寻址开销)
# 可配置:dfs.blocksize = 134217728 (128MB)
#
# 为什么不是1GB?Block太大会导致:
# ① 并行度低(一个Block一个Map Task,Block越大Task越少)
# ② 单Block传输时间长,网络故障重传代价大

Block大小的本质是'寻址时间 vs 传输时间'的平衡:磁盘读取数据有两个阶段——寻址(定位到数据位置)和传输(读取数据内容)。Block大小要保证传输时间远大于寻址时间(经验值是100:1),这样寻址开销可以忽略不计。当前磁盘传输速率约100MB/s,寻址时间约10ms,所以最优Block大小 = 100MB/s × 10ms × 100 ≈ 100MB,取整就是128MB。

3副本放置策略——机架感知

HDFS默认每个Block存3个副本。关键是这3个副本放在哪里——不是随机放的,而是有策略的:

副本放置位置原因
第1个副本写入请求所在的DataNode(或最近的DN)减少网络传输,写入最快
第2个副本不同机架的一个DataNode跨机架容灾——整个机架断电也不丢数据
第3个副本和第2个副本同机架的另一个DataNode同机架传输快,平衡性能和可靠性
# 机架感知示例
#
# 机架A:  DN-1  DN-2  DN-3
# 机架B:  DN-4  DN-5  DN-6
#
# 客户端在DN-1上写入一个文件:
# 副本1: DN-1(本机,最快)
# 副本2: DN-4(机架B,跨机架容灾)
# 副本3: DN-5(机架B的另一台,同机架传输快)
#
# 这样的好处:
# ① 机架A整个断电:DN-4和DN-5上还有数据
# ② 机架B整个断电:DN-1上还有数据
# ③ 写入时只需跨一次机架网络(副本1到副本2)
#    副本2到副本3在同机架内传输,很快

HDFS写入流程——Pipeline写入

# 客户端写一个文件到HDFS的完整流程
#
# 1. 客户端向NameNode请求写入
# 2. NameNode检查权限、路径合法性
#    返回应该写到哪些DataNode(根据机架感知选择)
# 3. 客户端把文件切成Block(128MB一块)
# 4. 对每个Block,建立Pipeline:
#    客户端 → DN-1 → DN-4 → DN-5
#    数据以Packet(64KB)为单位流式传输
#    DN-1收到Packet后同时写本地+转发给DN-4
#    DN-4收到后写本地+转发给DN-5
# 5. 所有副本写完后,DN-5向DN-4发ACK
#    DN-4向DN-1发ACK,DN-1向客户端发ACK
# 6. 客户端收到ACK后开始下一个Block
# 7. 所有Block写完后,客户端通知NameNode写入完成

HDFS读取流程——就近读取

# 客户端读一个文件的完整流程
#
# 1. 客户端向NameNode请求文件的Block列表
# 2. NameNode返回每个Block的副本位置列表
#    按距离排序(本机 > 同机架 > 跨机架)
# 3. 客户端从最近的DataNode读取每个Block
#    Block-1: 从DN-1读(本机,最快)
#    Block-2: 从DN-4读(同机架)
# 4. 如果某个DN读取失败,自动切换到下一个副本
#    比如DN-1挂了,自动从DN-3读Block-1
# 5. 客户端把所有Block拼接成完整文件
#
# 关键优化:数据本地性(Data Locality)
# MapReduce/Spark调度Task时,优先把Task分配到
# 数据所在的DataNode上,避免网络传输
# 这就是为什么Hadoop强调'移动计算而不是移动数据'

实战经验:Block大小对Spark作业的影响

在某知名出海硬件品牌用EMR时,有一个数据源产生的文件特别小(每个文件只有1-5MB)。Spark读取这些文件时,每个小文件对应一个Partition,导致Task数量爆炸(10万个Task),调度开销比计算本身还大,作业跑了2小时。

优化方案:① 在HDFS层面用CombineInputFormat合并小文件输入,把多个小文件合成一个InputSplit(目标128MB);② 在Spark层面用spark.sql.files.maxPartitionBytes控制单Partition大小;③ 从源头解决——调整上游产生大文件而不是小文件。优化后Task数量从10万降到200个,作业从2小时降到15分钟。

教训:Block大小不只是HDFS的配置问题,它直接影响下游Spark/Hive的作业性能。小文件太多会导致Task爆炸和NameNode内存压力(第4篇详讲)。

面试高频追问

追问一:Block大小可以改吗?改了有什么影响?

可以改,通过dfs.blocksize配置。改大(比如256MB)适合大文件场景——减少Block数量、减轻NameNode压力,但并行度降低。改小(比如64MB)适合需要高并行度的场景,但Block数量增多、NameNode内存压力增大。一般保持默认128MB就好,除非有明确的优化需求。

追问二:一个Block不满128MB怎么办?

不会浪费空间。如果一个文件只有10MB,它只占一个Block,这个Block在磁盘上实际只占10MB而不是128MB。128MB是'最大'大小,不是'固定'大小。但这个Block在NameNode的元数据中还是占一条记录(约150字节),所以小文件的问题在于NameNode元数据膨胀,不在于磁盘浪费。

追问三:副本数可以改吗?什么时候改?

可以按文件或目录设置不同的副本数。核心数据(数仓表)建议3副本;临时数据(中间结果)可以设1-2副本节省空间;对于HDFS纠删码(Erasure Coding),可以用更低的冗余率(1.4倍 vs 3倍)提供同等可靠性,第5篇详讲。

总结

HDFS的Block和副本面试讲清楚三个层面:① Block为什么是128MB(寻址时间vs传输时间的平衡);② 3副本放置策略(机架感知——第1个副本本机、第2个跨机架、第3个和第2个同机架);③ 读写流程(写是Pipeline流式传输、读是就近读取+自动故障切换)。能讲出Block大小对Spark作业性能的实际影响和优化案例,面试官会觉得你不只背了原理,还有实战理解。

下一篇聊NameNode的高可用:NameNode挂了整个HDFS就瘫痪了,怎么保证它不挂?

你们HDFS的Block大小是多少?改过吗?评论区聊聊~

关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长

刷题进度保存在当前浏览器