前言:一个关于"大"的故事
还记得你第一次听到"大数据"这个词是什么时候吗?
反正我记得很清楚——那是在一次技术分享会上,一位架构师大佬在台上慷慨激昂地说:"我们的数据量已经达到了PB级别,传统数据库已经无法满足需求了,我们需要引入Hadoop生态……"
当时的我,内心OS是这样的:
"PB是1000TB吗?那得存多少资源啊"
"我们不都是交易和用户数据吗,有这么多数据量吗?"
"Hadoop听说过?不知道好用不,坑多不多"
多年后的今天,当我终于搞懂了这些概念,并且成功地用Hadoop把集群搞崩了几次之后,我觉得是时候写一个系列来帮助大家——从入门到放弃。
本系列计划分三篇:
- 第一篇(本篇):Hadoop概述 + HDFS,先搞懂大象长什么样
- 第二篇:MapReduce,教大象干活
- 第三篇:YARN + 生态圈,给大象找一群小伙伴
好了,让我们开始吧。
一、先搞清楚:什么是大数据?
在讲Hadoop之前,我们得先搞清楚——什么是"大数据"?
1.1 数据量的进化史
1.2 大数据的4V特征
小草说,大数据最大的特点就是"大"。它有一个经典的"4V"定义,让我用程序员能理解的方式翻译一下:
1.3 传统方案为什么不行了?
让我们来看一个残酷的现实对比:
| 场景 | 传统方案 | 会发生什么 |
|---|---|---|
| 存100TB日志 | 买一台超级服务器 | 💸 钱包:我裂开了 |
| 分析1PB数据 | 用MySQL跑SQL | ⏰ 三天后:还在执行中... |
| 处理实时数据流 | 单机程序处理 | 💀 服务器:我先走一步 |
| 数据备份容灾 | RAID磁盘阵列 | 🔥 机房着火,全剧终 |
这时候,一头大象走了过来,它的名字叫——Hadoop。
二、Hadoop:一头来自Yahoo的大象
2.1 Hadoop的身世之谜
🐘 冷知识:Hadoop这个名字来源于Doug Cutting儿子的一个黄色毛绒玩具大象。所以每当你在凌晨三点调试Hadoop集群的时候,记住——你是在伺候一个毛绒玩具。
2.2 Hadoop的核心架构
Hadoop不是一个单独的软件,而是一个生态系统。但它的核心就三个东西:
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的设计哲学可以用一句话概括:用一堆便宜货干一件牛逼的事。
HDFS的核心设计假设:
| 假设 | 说明 | 设计决策 |
|---|---|---|
| 硬件会坏 | 几千台机器,天天有机器挂 | 数据默认存3份 |
| 文件巨大 | 都是GB/TB级别的大文件 | 块大小默认128MB |
| 一次写入多次读取 | 写完就不改了 | 不支持随机写入 |
| 移动计算比移动数据便宜 | 数据太大搬不动 | 计算向数据靠拢 |
3.2 HDFS架构:主从结构
存储元数据
文件目录结构
文件与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上传一个文件时,发生了什么?
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份怎么放也有讲究:
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读写流程详解
写文件流程
返回可写的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
读文件流程
按距离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的优缺点
让我们客观地评价一下这头大象:
节点挂了自动恢复] 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的阿喀琉斯之踵
这个问题太经典了,必须单独拿出来说。
解决方案:
| 方案 | 说明 |
|---|---|
| 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.txt4.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方案:
正在服务] 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 | 分布式协调服务,用于选主 |
| ZKFC | ZooKeeper故障转移控制器,监控NN健康状态 |
故障切换流程:
(避免脑裂) ZKFC2->>SNN: 提升为Active SNN->>SNN: 切换为Active状态 Note over SNN: 新的Active NN开始服务✅
六、HDFS Federation:联邦机制
当数据量大到一个NameNode也扛不住时(元数据太多),就需要Federation了:
/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。
七、本篇小结
让我们用一张图总结一下今天学到的内容:
八、写在最后
到这里,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不适合存什么样的数据?
大量小文件、需要低延迟访问的数据、需要频繁修改的数据。
参考资料
- 《Hadoop权威指南》
- 《大数据Hadoop 3.x分布式处理实战》
- 小草说----大数据和机器学习为什么这样火
- 尚硅谷Hadoop教程