一个文件上传后被切成了3份——HDFS的Block、副本和存储原理
本文目录
先说结论
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年大数据老兵,聊技术、聊职场、聊成长