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

outlier 12 分钟 4647 字 11 0

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

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

反正我记得很清楚——那是在一次技术分享会上,一位架构师大佬在台上慷慨激昂地说:"我们的数据量已经达到了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<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分布式协调服务,用于选主
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真的死了<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:教大象干活》


附录:常见面试题

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

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

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

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

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

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

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

参考资料

评论

还没有评论,来说点什么吧。

发表评论

Ctrl + Enter 快速提交
Enter 搜索Esc 关闭