前言:一个关于"大"的故事
还记得你第一次听到"大数据"这个词是什么时候吗?
反正我记得很清楚——那是在一次技术分享会上,一位架构师大佬在台上慷慨激昂地说:"我们的数据量已经达到了PB级别,传统数据库已经无法满足需求了,我们需要引入Hadoop生态……"
当时的我,内心OS是这样的:
"PB是1000TB吗?那得存多少资源啊"
"我们不都是交易和用户数据吗,有这么多数据量吗?"
"Hadoop听说过?不知道好用不,坑多不多"
多年后的今天,当我终于搞懂了这些概念,并且成功地用Hadoop把集群搞崩了几次之后,我觉得是时候写一个系列来帮助大家——从入门到放弃。
本系列计划分三篇:
- 第一篇(本篇):Hadoop概述 + HDFS,先搞懂大象长什么样
- 第二篇:MapReduce,教大象干活
- 第三篇: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<br/>Hadoop Distributed File System<br/>分布式文件系统]
end
subgraph 计算层["⚙️ 计算层"]
MR[MapReduce<br/>分布式计算框架<br/>把大任务拆成小任务]
end
subgraph 资源层["🎛️ 资源调度层"]
YARN[YARN<br/>Yet Another Resource Negotiator<br/>集群资源管理]
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<br/>存储元数据<br/>文件目录结构<br/>文件与Block映射]
SNN[Secondary NameNode<br/>定期合并镜像<br/>不是热备!]
end
subgraph Slaves["💪 从节点 Slaves"]
DN1[DataNode 1<br/>存储实际数据块]
DN2[DataNode 2<br/>存储实际数据块]
DN3[DataNode 3<br/>存储实际数据块]
DN4[DataNode N...<br/>可以有很多很多]
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<br/>300MB ÷ 128MB = 3个Block
NN->>C: 好的,Block1放DN1/DN2/DN3<br/>Block2放DN2/DN3/DN1<br/>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<br/>Block1副本1]
N2[Node2<br/>Block1副本2]
end
subgraph 机架2["🗄️ 机架 Rack2"]
N3[Node3<br/>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创建文件记录<br/>返回可写的DataNode列表]
E --> F[4. Client建立Pipeline<br/>DN1→DN2→DN3]
F --> G[5. Client以Packet为单位发送数据<br/>64KB一个Packet]
G --> H[6. 数据沿Pipeline流动<br/>DN1写完传DN2,DN2写完传DN3]
H --> I[7. ACK反向返回]
I --> J{还有数据?}
J --> |Yes| G
J --> |No| K[8. Client通知NameNode<br/>文件写入完成]
K --> L[9. NameNode更新元数据<br/>提交文件]
end
style A fill:#4ecdc4
style L fill:#4ecdc4
style D fill:#ff6b6b
读文件流程
flowchart TB
subgraph 读流程["📖 HDFS读文件流程"]
A[1. Client请求读取文件] --> B[2. NameNode返回Block位置列表<br/>按距离Client远近排序]
B --> C[3. Client选择最近的DataNode]
C --> D[4. 建立连接,读取Block1]
D --> E{Block1读完?}
E --> |No| D
E --> |Yes| F{还有更多Block?}
F --> |Yes| G[5. 关闭当前连接<br/>读取下一个Block]
G --> C
F --> |No| H[6. 关闭连接<br/>合并所有Block<br/>文件读取完成]
end
style A fill:#ffe66d
style H fill:#ffe66d
注意:Client直接和DataNode交互读写数据,NameNode只负责告诉Client数据在哪。这样NameNode不会成为性能瓶颈。
3.5 HDFS的优缺点
让我们客观地评价一下这头大象:
graph TB
subgraph 优点["✅ 优点"]
A1[高容错性] --> A1D[数据自动备份<br/>节点挂了自动恢复]
A2[高吞吐量] --> A2D[适合批量处理<br/>不追求低延迟]
A3[支持超大文件] --> A3D[GB/TB/PB级别<br/>轻松应对]
A4[简单一致性模型] --> A4D[一次写入多次读取<br/>逻辑简单]
A5[横向扩展] --> A5D[加机器就能扩容<br/>线性扩展]
end
subgraph 缺点["❌ 缺点"]
B1[不适合低延迟] --> B1D[毫秒级访问?<br/>别想了]
B2[不支持随机写] --> B2D[只能追加<br/>不能修改中间内容]
B3[小文件是噩梦] --> B3D[每个文件占NameNode内存<br/>1亿小文件=内存爆炸]
B4[NameNode单点问题] --> B4D[虽然有HA方案<br/>但还是要小心]
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<br/>正在服务]
end
subgraph Standby["备节点"]
SNN[Standby NameNode<br/>热备待命]
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 | 分布式协调服务,用于选主 |
| ZKFC | ZooKeeper故障转移控制器,监控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真的死了<br/>(避免脑裂)
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<br/>/user/...]
end
subgraph NS2["命名空间2"]
NN2[NameNode2<br/>/logs/...]
end
subgraph NS3["命名空间3"]
NN3[NameNode3<br/>/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:教大象干活》
附录:常见面试题
既然都学了,顺便准备一下面试吧:
HDFS的Block大小为什么是128MB而不是更小?
减少寻址时间占比。如果Block太小,寻址时间就会占传输时间的大头,效率低下。
NameNode的SafeMode是什么?
启动时的只读模式,等待DataNode汇报Block信息,达到最小副本要求后自动退出。
HDFS的副本放置策略是什么?
第一副本放本地/随机节点,第二副本放同机架不同节点,第三副本放不同机架。
Secondary NameNode的作用是什么?
定期合并FsImage和EditLog,减轻NameNode重启时的恢复时间,不是热备!
HDFS不适合存什么样的数据?
大量小文件、需要低延迟访问的数据、需要频繁修改的数据。
参考资料
评论
还没有评论,来说点什么吧。