搜 索

大数据之Hadoop从入门到放弃之HDFS

  • 225阅读
  • 2022年07月31日
  • 0评论
首页 / AI/大数据 / 正文

前言:一个关于"大"的故事

还记得你第一次听到"大数据"这个词是什么时候吗?

反正我记得很清楚——那是在一次技术分享会上,一位架构师大佬在台上慷慨激昂地说:"我们的数据量已经达到了PB级别,传统数据库已经无法满足需求了,我们需要引入Hadoop生态……"

当时的我,内心OS是这样的:

"PB是1000TB吗?那得存多少资源啊"

"我们不都是交易和用户数据吗,有这么多数据量吗?"

"Hadoop听说过?不知道好用不,坑多不多"

多年后的今天,当我终于搞懂了这些概念,并且成功地用Hadoop把集群搞崩了几次之后,我觉得是时候写一个系列来帮助大家——从入门到放弃

本系列计划分三篇:

  1. 第一篇(本篇):Hadoop概述 + HDFS,先搞懂大象长什么样
  2. 第二篇:MapReduce,教大象干活
  3. 第三篇:YARN + 生态圈,给大象找一群小伙伴

好了,让我们开始吧。


一、先搞清楚:什么是大数据?

在讲Hadoop之前,我们得先搞清楚——什么是"大数据"?

1.1 数据量的进化史

timeline title 数据存储单位进化史(程序员的噩梦升级之路) KB时代 : 一个Word文档 : 那时候硬盘还是MB级别 : 生活是美好的 MB时代 : 一首MP3 : 一张高清照片 : Excel还能打开 GB时代 : 一部电影 : 一个游戏安装包 : 数据库开始卡了 TB时代 : 公司一年的日志 : 电商平台一天的交易 : DBA开始秃头 PB时代 : 互联网巨头的日常 : 1PB = 1024TB : 单机已死,集群当立 EB/ZB时代 : 全球数据总量 : 人类已经无法想象 : Hadoop:终于轮到我了

1.2 大数据的4V特征

小草说,大数据最大的特点就是"大"。它有一个经典的"4V"定义,让我用程序员能理解的方式翻译一下:

mindmap root((大数据4V)) Volume 数据量大 不是你那几个G的小电影 是TB/PB/EB级别 单机存不下,必须分布式 Velocity 速度快 数据产生速度快 数据处理要求快 双十一一秒几十万订单 Variety 种类多 结构化:数据库表 半结构化:JSON/XML 非结构化:图片/视频/日志 Value 价值密度低 一堆数据里找有用的 就像大海捞针 但针是金子做的

1.3 传统方案为什么不行了?

让我们来看一个残酷的现实对比:

场景传统方案会发生什么
存100TB日志买一台超级服务器💸 钱包:我裂开了
分析1PB数据用MySQL跑SQL⏰ 三天后:还在执行中...
处理实时数据流单机程序处理💀 服务器:我先走一步
数据备份容灾RAID磁盘阵列🔥 机房着火,全剧终

这时候,一头大象走了过来,它的名字叫——Hadoop


二、Hadoop:一头来自Yahoo的大象

2.1 Hadoop的身世之谜

timeline title Hadoop发展史:从玩具到生产力工具 2003-2004 : Google发表GFS和MapReduce论文 : 互联网公司:这是什么神仙操作? 2005 : Doug Cutting开始实现开源版本 : 以儿子的玩具大象命名:Hadoop : 是的,就是一个毛绒玩具的名字 2006 : Hadoop成为Apache顶级项目 : Yahoo开始大规模使用 : 大象开始干活了 2008 : Hadoop打破TB级数据排序世界纪录 : 大厂们:真香 2011 : Hadoop 1.0发布 : 生态开始爆发 2013 : Hadoop 2.0发布,引入YARN : 从此不只是批处理 2017 : Hadoop 3.0发布 : 支持纠删码、更省存储 至今 : 虽然有Spark/Flink等新秀 : 但HDFS依然是存储层的王者
🐘 冷知识:Hadoop这个名字来源于Doug Cutting儿子的一个黄色毛绒玩具大象。所以每当你在凌晨三点调试Hadoop集群的时候,记住——你是在伺候一个毛绒玩具。

2.2 Hadoop的核心架构

Hadoop不是一个单独的软件,而是一个生态系统。但它的核心就三个东西:

graph TB subgraph Hadoop核心三件套["🐘 Hadoop核心架构"] direction TB subgraph 存储层["📦 存储层"] HDFS[HDFS
Hadoop Distributed File System
分布式文件系统] end subgraph 计算层["⚙️ 计算层"] MR[MapReduce
分布式计算框架
把大任务拆成小任务] end subgraph 资源层["🎛️ 资源调度层"] YARN[YARN
Yet Another Resource Negotiator
集群资源管理] end end YARN --> |"管理计算资源"|MR MR --> |"读写数据"|HDFS style HDFS fill:#4ecdc4,stroke:#333,stroke-width:2px style MR fill:#ff6b6b,stroke:#333,stroke-width:2px style YARN fill:#ffe66d,stroke:#333,stroke-width:2px

用人话说就是:

  • HDFS:超大号的网盘,可以存海量数据
  • MapReduce:把一个大任务拆成很多小任务,分给很多机器一起干
  • YARN:包工头,负责分配谁干什么活

本篇我们重点聊HDFS,MapReduce和YARN留给后面两篇。


三、HDFS:一个能存下整个互联网的文件系统

3.1 设计理念:便宜、大碗、不怕坏

HDFS的设计哲学可以用一句话概括:用一堆便宜货干一件牛逼的事

graph LR subgraph 传统思路["❌ 传统土豪思路"] A[买超贵的存储阵列] --> B[几百万一台] B --> C[坏了就完蛋] end subgraph HDFS思路["✅ HDFS穷人思路"] D[买一堆便宜PC服务器] --> E[几千块一台] E --> F[坏了就换一台] F --> G[数据有备份,不怕] end style A fill:#ff6b6b style D fill:#4ecdc4

HDFS的核心设计假设:

假设说明设计决策
硬件会坏几千台机器,天天有机器挂数据默认存3份
文件巨大都是GB/TB级别的大文件块大小默认128MB
一次写入多次读取写完就不改了不支持随机写入
移动计算比移动数据便宜数据太大搬不动计算向数据靠拢

3.2 HDFS架构:主从结构

graph TB subgraph Client["👨‍💻 客户端"] C[HDFS Client] end subgraph Master["🧠 主节点 Master"] NN[NameNode
存储元数据
文件目录结构
文件与Block映射] SNN[Secondary NameNode
定期合并镜像
不是热备!] end subgraph Slaves["💪 从节点 Slaves"] DN1[DataNode 1
存储实际数据块] DN2[DataNode 2
存储实际数据块] DN3[DataNode 3
存储实际数据块] DN4[DataNode N...
可以有很多很多] end C --> |"1. 请求文件位置"|NN NN --> |"2. 返回Block位置"|C C --> |"3. 直接读写数据"|DN1 C --> |"3. 直接读写数据"|DN2 DN1 --> |"心跳 + Block报告"|NN DN2 --> |"心跳 + Block报告"|NN DN3 --> |"心跳 + Block报告"|NN NN -.-> |"元数据检查点"|SNN style NN fill:#ff6b6b,stroke:#333,stroke-width:2px style SNN fill:#ffa500,stroke:#333,stroke-width:2px style DN1 fill:#4ecdc4,stroke:#333 style DN2 fill:#4ecdc4,stroke:#333 style DN3 fill:#4ecdc4,stroke:#333 style DN4 fill:#4ecdc4,stroke:#333

角色说明

NameNode(名字节点)

  • HDFS的"大脑",存储所有元数据
  • 知道每个文件被切成了哪些块
  • 知道每个块存在哪些DataNode上
  • 单点故障风险:它挂了,整个集群就废了(所以后来有了HA方案)

DataNode(数据节点)

  • HDFS的"苦力",存储实际数据
  • 定期向NameNode汇报自己还活着(心跳)
  • 定期汇报自己有哪些Block

Secondary NameNode

  • 不是NameNode的热备份!
  • 只是帮NameNode合并编辑日志,减轻负担
  • 名字起得很有误导性,很多人第一次都会理解错

3.3 文件是怎么存的?Block机制

当你往HDFS上传一个文件时,发生了什么?

sequenceDiagram participant C as 客户端 participant NN as NameNode participant DN1 as DataNode1 participant DN2 as DataNode2 participant DN3 as DataNode3 Note over C: 要上传一个300MB的文件 C->>NN: 老大,我要上传文件hadoop.tar.gz NN->>NN: 计算需要切成几个Block
300MB ÷ 128MB = 3个Block NN->>C: 好的,Block1放DN1/DN2/DN3
Block2放DN2/DN3/DN1
Block3放DN3/DN1/DN2 Note over C,DN3: 开始上传Block1(128MB) C->>DN1: 传输Block1 DN1->>DN2: 复制Block1(Pipeline) DN2->>DN3: 复制Block1 DN3-->>DN2: ACK DN2-->>DN1: ACK DN1-->>C: Block1完成 Note over C,DN3: 同样方式上传Block2、Block3... C->>NN: 文件上传完成 NN->>NN: 更新元数据

Block的存储策略

每个Block默认存3份(副本因子=3),这3份怎么放也有讲究:

graph TB subgraph 机架1["🗄️ 机架 Rack1"] N1[Node1
Block1副本1] N2[Node2
Block1副本2] end subgraph 机架2["🗄️ 机架 Rack2"] N3[Node3
Block1副本3] N4[Node4] end subgraph 机架感知策略["📋 副本放置策略"] S1[第1份:本地机架某节点] S2[第2份:本地机架另一节点] S3[第3份:远程机架某节点] end style N1 fill:#ff6b6b,stroke:#333 style N2 fill:#ff6b6b,stroke:#333 style N3 fill:#ff6b6b,stroke:#333 S1 --> N1 S2 --> N2 S3 --> N3

为什么这么放?

策略原因
2份在同一机架同机架网络快,写入效率高
1份在不同机架防止整个机架挂掉(电源故障、交换机故障)

这就是所谓的机架感知(Rack Awareness)策略。

3.4 HDFS读写流程详解

写文件流程

flowchart TB subgraph 写流程["📝 HDFS写文件流程"] A[1. Client请求创建文件] --> B[2. NameNode检查权限和路径] B --> C{检查通过?} C --> |No| D[返回错误] C --> |Yes| E[3. NameNode创建文件记录
返回可写的DataNode列表] E --> F[4. Client建立Pipeline
DN1→DN2→DN3] F --> G[5. Client以Packet为单位发送数据
64KB一个Packet] G --> H[6. 数据沿Pipeline流动
DN1写完传DN2,DN2写完传DN3] H --> I[7. ACK反向返回] I --> J{还有数据?} J --> |Yes| G J --> |No| K[8. Client通知NameNode
文件写入完成] K --> L[9. NameNode更新元数据
提交文件] end style A fill:#4ecdc4 style L fill:#4ecdc4 style D fill:#ff6b6b

读文件流程

flowchart TB subgraph 读流程["📖 HDFS读文件流程"] A[1. Client请求读取文件] --> B[2. NameNode返回Block位置列表
按距离Client远近排序] B --> C[3. Client选择最近的DataNode] C --> D[4. 建立连接,读取Block1] D --> E{Block1读完?} E --> |No| D E --> |Yes| F{还有更多Block?} F --> |Yes| G[5. 关闭当前连接
读取下一个Block] G --> C F --> |No| H[6. 关闭连接
合并所有Block
文件读取完成] end style A fill:#ffe66d style H fill:#ffe66d

注意:Client直接和DataNode交互读写数据,NameNode只负责告诉Client数据在哪。这样NameNode不会成为性能瓶颈。

3.5 HDFS的优缺点

让我们客观地评价一下这头大象:

graph TB subgraph 优点["✅ 优点"] A1[高容错性] --> A1D[数据自动备份
节点挂了自动恢复] A2[高吞吐量] --> A2D[适合批量处理
不追求低延迟] A3[支持超大文件] --> A3D[GB/TB/PB级别
轻松应对] A4[简单一致性模型] --> A4D[一次写入多次读取
逻辑简单] A5[横向扩展] --> A5D[加机器就能扩容
线性扩展] end subgraph 缺点["❌ 缺点"] B1[不适合低延迟] --> B1D[毫秒级访问?
别想了] B2[不支持随机写] --> B2D[只能追加
不能修改中间内容] B3[小文件是噩梦] --> B3D[每个文件占NameNode内存
1亿小文件=内存爆炸] B4[NameNode单点问题] --> B4D[虽然有HA方案
但还是要小心] end style A1 fill:#4ecdc4 style A2 fill:#4ecdc4 style A3 fill:#4ecdc4 style A4 fill:#4ecdc4 style A5 fill:#4ecdc4 style B1 fill:#ff6b6b style B2 fill:#ff6b6b style B3 fill:#ff6b6b style B4 fill:#ff6b6b

3.6 小文件问题:HDFS的阿喀琉斯之踵

这个问题太经典了,必须单独拿出来说。

graph LR subgraph 问题["😱 小文件问题"] A[1亿个1KB小文件] --> B[1亿个Block元数据] B --> C[NameNode内存占用约30GB] C --> D[内存爆炸💥] end subgraph 同样大小["📊 对比"] E[100个1GB大文件] --> F[约800个Block] F --> G[元数据几乎可忽略] end style D fill:#ff0000,color:#fff style G fill:#00ff00

解决方案

方案说明
HAR文件把小文件打包成一个归档文件
SequenceFile把小文件合并成一个大的二进制文件
CombineFileInputFormat在计算时合并多个小文件
根本解决源头上避免产生太多小文件

四、HDFS Shell命令:开始调戏大象

理论讲完了,让我们来点实际操作。HDFS提供了类似Linux的shell命令:

4.1 基础命令速查表

# 查看目录
hdfs dfs -ls /
hdfs dfs -ls -R /user  # 递归查看

# 创建目录
hdfs dfs -mkdir /user/joey
hdfs dfs -mkdir -p /user/joey/data/logs  # 递归创建

# 上传文件
hdfs dfs -put localfile.txt /user/joey/
hdfs dfs -copyFromLocal bigdata.csv /user/joey/data/

# 下载文件
hdfs dfs -get /user/joey/result.txt ./
hdfs dfs -copyToLocal /user/joey/data/ ./local_dir/

# 查看文件内容
hdfs dfs -cat /user/joey/hello.txt
hdfs dfs -tail /user/joey/bigfile.log  # 看最后1KB
hdfs dfs -head /user/joey/bigfile.log  # 看开头

# 删除
hdfs dfs -rm /user/joey/temp.txt
hdfs dfs -rm -r /user/joey/old_data/  # 递归删除
hdfs dfs -rm -skipTrash /user/joey/trash.txt  # 跳过回收站直接删

# 移动/重命名
hdfs dfs -mv /user/joey/old.txt /user/joey/new.txt

# 复制
hdfs dfs -cp /user/joey/data.txt /user/backup/

# 查看文件大小
hdfs dfs -du -h /user/joey/  # 人类可读格式
hdfs dfs -df -h  # 查看HDFS容量

# 修改副本数
hdfs dfs -setrep -w 2 /user/joey/data.txt

# 查看文件状态
hdfs dfs -stat "%F %u:%g %b %y %n" /user/joey/test.txt

4.2 管理命令

# 查看集群状态
hdfs dfsadmin -report

# 安全模式操作
hdfs dfsadmin -safemode get     # 查看是否在安全模式
hdfs dfsadmin -safemode enter   # 进入安全模式
hdfs dfsadmin -safemode leave   # 离开安全模式

# 刷新节点
hdfs dfsadmin -refreshNodes

# 检查文件系统健康
hdfs fsck /
hdfs fsck /user/joey -files -blocks -locations  # 详细信息

五、HDFS的高可用(HA)方案

前面说了,NameNode是单点故障。那怎么办?Hadoop 2.0引入了HA方案:

graph TB subgraph HA架构["🔄 HDFS HA架构"] subgraph Active["主节点"] ANN[Active NameNode
正在服务] end subgraph Standby["备节点"] SNN[Standby NameNode
热备待命] end subgraph JN["共享存储"] JN1[JournalNode1] JN2[JournalNode2] JN3[JournalNode3] end subgraph ZK["故障检测"] ZK1[ZooKeeper集群] ZKFC1[ZKFC] ZKFC2[ZKFC] end subgraph DN["数据节点"] DN1[DataNode1] DN2[DataNode2] DN3[DataNode3] end end ANN --> |"写EditLog"|JN1 ANN --> |"写EditLog"|JN2 ANN --> |"写EditLog"|JN3 SNN --> |"读EditLog同步"|JN1 SNN --> |"读EditLog同步"|JN2 SNN --> |"读EditLog同步"|JN3 ZKFC1 --> |"监控"|ANN ZKFC2 --> |"监控"|SNN ZKFC1 --> ZK1 ZKFC2 --> ZK1 DN1 --> |"同时汇报"|ANN DN1 --> |"同时汇报"|SNN DN2 --> |"同时汇报"|ANN DN2 --> |"同时汇报"|SNN style ANN fill:#4ecdc4,stroke:#333,stroke-width:2px style SNN fill:#ffe66d,stroke:#333,stroke-width:2px

核心组件说明

组件作用
Active NameNode当前提供服务的NameNode
Standby NameNode热备节点,随时准备接管
JournalNode共享编辑日志,保证两个NN数据一致
ZooKeeper分布式协调服务,用于选主
ZKFCZooKeeper故障转移控制器,监控NN健康状态

故障切换流程

sequenceDiagram participant ZKFC1 as ZKFC(Active) participant ANN as Active NN participant ZK as ZooKeeper participant ZKFC2 as ZKFC(Standby) participant SNN as Standby NN Note over ANN: Active NN突然挂了💀 ZKFC1->>ANN: 健康检查失败 ZKFC1->>ZK: 报告:Active NN死了 ZK->>ZKFC2: 通知:你的机会来了 ZKFC2->>ZKFC2: 确认Active真的死了
(避免脑裂) ZKFC2->>SNN: 提升为Active SNN->>SNN: 切换为Active状态 Note over SNN: 新的Active NN开始服务✅

六、HDFS Federation:联邦机制

当数据量大到一个NameNode也扛不住时(元数据太多),就需要Federation了:

graph TB subgraph Federation["🏛️ HDFS Federation架构"] subgraph NS1["命名空间1"] NN1[NameNode1
/user/...] end subgraph NS2["命名空间2"] NN2[NameNode2
/logs/...] end subgraph NS3["命名空间3"] NN3[NameNode3
/data/...] end subgraph 共享存储池["📦 共享DataNode池"] DN1[DataNode1] DN2[DataNode2] DN3[DataNode3] DN4[DataNode4] end end NN1 --> DN1 NN1 --> DN2 NN2 --> DN2 NN2 --> DN3 NN3 --> DN3 NN3 --> DN4 NN1 --> DN4 style NN1 fill:#ff6b6b style NN2 fill:#4ecdc4 style NN3 fill:#ffe66d

Federation的思路就是:横向扩展NameNode,每个NameNode管理一部分命名空间,但共享同一批DataNode。


七、本篇小结

让我们用一张图总结一下今天学到的内容:

graph TB subgraph 今日收获["📚 本篇知识点总结"] A[大数据4V特征] --> B[Hadoop是什么] B --> C[HDFS核心概念] C --> D[架构设计] D --> D1[NameNode:大脑] D --> D2[DataNode:苦力] D --> D3[Block:数据切块] C --> E[读写流程] E --> E1[写:Client→NN→DN Pipeline] E --> E2[读:Client→NN→最近DN] C --> F[高级特性] F --> F1[副本策略:机架感知] F --> F2[高可用:HA方案] F --> F3[横向扩展:Federation] C --> G[注意事项] G --> G1[小文件问题] G --> G2[不支持随机写] G --> G3[不适合低延迟] end style A fill:#ff6b6b style B fill:#4ecdc4 style C fill:#ffe66d

八、写在最后

到这里,Hadoop系列的第一篇就结束了。我们认识了这头来自Yahoo的大象,了解了它的设计哲学,深入研究了HDFS这个分布式文件系统。

说实话,HDFS的设计理念在今天看来依然很优雅:

  • 用便宜的硬件做可靠的存储——这种"穷人思维"反而是最实用的
  • 数据冗余保证可靠性——不怕坏,大不了再复制一份
  • 计算向数据移动——山不过来,我就过去

下一篇,我们将学习如何让这头大象干活——MapReduce计算框架。

预告一下下一篇的内容:

  • MapReduce的编程模型
  • Word Count是怎么被Map和Reduce的
  • Shuffle过程详解(重点!)
  • MapReduce的优化技巧

如果你现在已经感觉到了一丝丝头疼,那恭喜你——你正式踏上了大数据的不归路。

下一篇见!🐘


本文作者:一个在HDFS上丢过数据的程序员

上一次丢数据原因:以为Secondary NameNode是热备

下期预告:《大数据之Hadoop从入门到放弃(二)——MapReduce:教大象干活》


附录:常见面试题

既然都学了,顺便准备一下面试吧:

  1. HDFS的Block大小为什么是128MB而不是更小?

    减少寻址时间占比。如果Block太小,寻址时间就会占传输时间的大头,效率低下。
  2. NameNode的SafeMode是什么?

    启动时的只读模式,等待DataNode汇报Block信息,达到最小副本要求后自动退出。
  3. HDFS的副本放置策略是什么?

    第一副本放本地/随机节点,第二副本放同机架不同节点,第三副本放不同机架。
  4. Secondary NameNode的作用是什么?

    定期合并FsImage和EditLog,减轻NameNode重启时的恢复时间,不是热备!
  5. HDFS不适合存什么样的数据?

    大量小文件、需要低延迟访问的数据、需要频繁修改的数据。

参考资料

评论区
暂无评论
avatar