1. Day6到底该学什么:找准学习路上的关键锚点
1.1 前5天走完了什么路
很多刚开始接触大数据开发的人,最容易犯的一个毛病就是:今天翻到一篇讲Spark的文章,觉得挺酷;明天又看到Flink的实时计算案例,又开始纠结要不要转方向。结果学了半个月,连数据到底存在哪、怎么取出来都没搞明白。
按照一套比较稳的学习节奏,前5天应该做的事情大概是这样的:
- Day1到Day2,补齐Linux基础。因为大数据生态里的组件几乎全部跑在Linux上,你至少要会用命令行操作文件、写简单的Shell脚本、配置环境变量、看懂系统日志。
- Day3到Day5,搞定Java和必要的开发环境。Java是大数据生态的第一语言,Hadoop本身是Java写的,Hive、HBase这些上层工具也都跑在JVM里。不需要Java水平有多高,但类、对象、集合、IO流、多线程这几个基础概念,后面写MapReduce和Spark程序时全都会用到。
到Day5结束的时候,你应该已经具备了这样的基本能力:能在Linux上装JDK、能独立写一个简单的Java程序并打包运行、对文件权限和网络端口有基本概念。这些是地基,看起来不起眼,但后面哪个环节出了问题,往往都能回到这里找到原因。
1.2 为什么第六天要死磕分布式存储
很多人第一次面对HDFS(Hadoop Distributed File System,分布式文件系统)的时候,心里冒出来的第一个念头是:这不就是个文件系统吗?跟我电脑上的文件夹有什么区别?
确实是文件系统,但这是“一群服务器拼在一起对外提供服务”的文件系统。它的核心场景是:数据量大到一台机器放不下,或者单台机器坏了数据就全没了,这时候需要把数据分散存放在多台机器上,并且让用户感觉还是在操作一个大文件夹。
Day6放在这个位置,意义就在这:HDFS是整个大数据技术栈里几乎一切计算的地基。你后面学的MapReduce、Hive、Spark、Flink,底层数据要么直接存在HDFS上,要么通过接口把HDFS当作数据源。地基不牢,后面盖什么楼都心慌。
而且基于云平台的大数据应用开发越来越普遍以后,很多人直接用云上的对象存储或者托管Hadoop集群,省去了自己搭环境的麻烦。但云平台只是把底层的分布式存储封装成了服务,核心的目录结构、数据分块、副本机制、读写原理,和HDFS是同一个套路。所以第六天把HDFS的原理和操作吃透,后面上云也好、用别的分布式存储也好,你都有一通百通的基础。
提示:这一天不要想着“我会用命令就行”,一定要动手把环境搭起来,亲手执行上传、下载、查看文件位置、体验一次节点故障后数据还能读出来。只有亲手做过,原理才真正长在你身上。
2. HDFS核心机制:先搞懂原理再动手,省下三倍调试时间
2.1 从“文件怎么存”说起:NameNode和DataNode的分工
HDFS采用的是典型的主从架构。集群里有两类角色:
- NameNode(主节点):负责管理整个文件系统的“目录和索引”。它不存具体数据,只记录每个文件被切成了多少个数据块、每个数据块存放在哪些机器上。打个比方,它就是图书馆的总目录,你要找哪本书,先查它。
- DataNode(从节点):真正存放数据块的地方。它负责处理客户端发来的读写请求,定期向NameNode汇报自己手里有哪些数据块。继续用图书馆的例子,它就是书架,书实际摆在书架上。
这个设计最核心的好处是:元数据和数据分离。NameNode不需要承担海量数据的读写压力,只需要维护一份轻量的元数据,就能管理PB级别的数据。DataNode可以横向扩展,机器不够了加几台,副本数不够了自动补齐。
Day6阶段最常见的困惑是:我把一个文件传到HDFS上,这个文件到底被切成了什么样?默认情况下,HDFS会按128MB(旧版本是64MB)的大小把文件切块,每个块独立存储。举个例子,一个300MB的文件会被切成3个块,前两个块各128MB,第三个块44MB。多副本模式下,每个块还会复制出多份放到不同机器上。
2.2 数据写入流程:一次读写背后发生了什么
理解了架构,再看读写流程就不难了。以写入过程为例,大致是这几步:
- 客户端向NameNode发起上传请求,告诉它“我要创建某个文件”。
- NameNode检查权限和路径合法性,允许创建,并告诉客户端应该往哪些DataNode上写。
- 客户端把文件按块切分,第一个块先写入第一个DataNode。
- 第一个DataNode写完以后,会自动把数据转发给第二个DataNode,第二个再转发给第三个,形成一条流水线。
- 所有副本都写完了,DataNode会向NameNode汇报消息。
这个流程的设计逻辑是:让数据在DataNode之间“接力”传输,而不是由客户端分别往每台机器传一遍。因为客户端通常在集群外部,带宽有限,如果每写一个副本都要客户端往外传一次,性能会成倍下降。接力传输的方式,充分利用了集群内部的带宽。
读取过程类似,客户端拿到文件块的位置信息后,会优先从最近的节点读取数据。好的分布式系统设计都有一个共同点:尽量让数据计算发生在数据所在的位置,减少网络传输开销。这个原则到后面学MapReduce、Spark时还会反复遇到。
2.3 副本机制:默认3副本背后的权衡逻辑
副本机制是HDFS可靠性最直接的保障。默认情况下每份数据存3个副本,放置策略大概是:
- 第1个副本:放在客户端所在的机器上(如果客户端不在集群内,就随机挑一台负载不高的节点)。
- 第2个副本:放在与第1个副本不同机架的某台机器上。
- 第3个副本:与第2个副本同机架,但是不同机器。
这样设计是为了同时兼顾“容灾”和“性能”。如果整个机架断电了,集群还有其他机架上的副本可供读取;如果某个副本参与计算,同机架内的网络传输速度又比跨机架快。
有人会问:那我是不是可以把副本数设置成1,省点磁盘?可以。在测试环境、伪分布式环境下,我经常就把副本数改成1,因为就一台机器,你让它把3份副本放到哪去?但生产环境请老老实实保留至少3副本,宁可多花点磁盘,也不要赌运气。
注意:副本数不是写死的,可以在配置里改,也可以在命令行里动态修改。但副本数越多,磁盘占用越高,写入时数据同步的开销也越大,它是个典型的“用空间和性能换安全”的权衡。学习阶段了解这些,后面设计存储方案时你才能跟别人讲清楚为什么这么定。
3. 环境搭建实操:从零开始跑起一个能用的HDFS
3.1 环境规划和版本选型
分布式存储听起来很高大上,但学习阶段完全没必要搭一个5台甚至10台机器的集群,一台普通的电脑就够了。我建议用伪分布式模式:一台机器上同时运行NameNode和DataNode进程,完整的分布式架构一个不缺,只是所有角色都挤在同一台机器上。
系统方面,优先选CentOS 7或者Ubuntu Server这类Linux发行版,也可以用虚拟机,也可以用云服务器。如果本机就是Windows/Mac,更省事的方案是装一个带Hadoop的Docker镜像。但我的建议是:虚拟机或者云服务器,老老实实走一遍原生安装流程,这对理解配置项和目录结构更有帮助。
版本选择上,Apache Hadoop 3.x是目前主流,我用的3.3.x系列。Java版本要注意,Hadoop 3.3需要JDK 8以上,配JDK 8最稳,配JDK 11也可以但没必要给自己添麻烦。下载时可以去Apache官网或者国内镜像站,压缩包大概几百MB,解压即用,不需要编译。
3.2 核心配置:三个文件改完就能启动
安装好JDK和Hadoop、配置好环境变量之后,真正要改的配置主要是三个文件,它们都在Hadoop安装目录的etc/hadoop下:
第一个是hadoop-env.sh,里面要指定JAVA_HOME,让Hadoop能找到Java环境。这一步不做,启动脚本会直接报错。
第二个是core-site.xml,核心配置,主要设置默认文件系统和临时目录:
<configuration> <property> <name>fs.defaultFS</name> <value>hdfs://localhost:9000</value> </property> <property> <name>hadoop.tmp.dir</name> <value>/opt/hadoop/tmp</value> </property> </configuration>fs.defaultFS指定了HDFS的访问入口,后面所有HDFS路径都会基于这个地址解析。hadoop.tmp.dir是Hadoop存放临时文件和数据的地方,默认值在/tmp下,系统一清理就出问题,所以一定要自己指定一个持久化目录。
第三个是hdfs-site.xml,核心是设置副本数:
<configuration> <property> <name>dfs.replication</name> <value>1</value> </property> <property> <name>dfs.namenode.name.dir</name> <value>file:///opt/hadoop/namenode</value> </property> <property> <name>dfs.datanode.data.dir</name> <value>file:///opt/hadoop/datanode</value> </property> </configuration>伪分布式模式下副本数必须设为1,因为只有一台DataNode,3副本永远无法满足,集群会一直警告副本缺失。NameNode的元数据目录和DataNode的数据存储目录也建议显式指定,避免默认路径不稳定。
3.3 启动、验证和格式化那点事
配置写好后,第一次启动HDFS前需要先执行格式化命令:
hdfs namenode -format格式化的作用是为NameNode创建初始的元数据存储结构。很多人会在启动报错时傻眼:明明配置都对,为什么起不来?十有八九是重复格式化,或者格式化后改了目录地址,导致NameNode和DataNode的集群ID对不上。
注意:
hdfs namenode -format只在第一次使用或需要重置集群时执行,别养成每次启动前都格式化一下的习惯,那会导致元数据全部丢失。
格式化完成后,用一条命令启动所有进程:
start-dfs.sh启动后输入jps查看Java进程,正常情况下能看到NameNode、DataNode、SecondaryNameNode三个进程。看到这三个进程,说明HDFS已经跑起来了。接着用浏览器打开http://localhost:9870,可以看到一个Web界面,里面有文件系统浏览、集群健康状态、DataNode列表等信息。这一步一定要做,后面你排查问题时,这个界面能帮大忙。
4. HDFS Shell实操:用命令把数据处理流程串起来
4.1 高频命令过关清单
HDFS的Shell命令跟Linux命令风格非常像,有Linux基础的话几乎不需要刻意背。下面这些是我认为学习阶段必须过关的高频命令:
# 查看根目录下的文件和目录 hdfs dfs -ls / # 创建目录(支持-p递归创建) hdfs dfs -mkdir -p /data/log # 把本地文件上传到HDFS hdfs dfs -put /opt/local_data.txt /data/log/ # 从HDFS下载文件到本地 hdfs dfs -get /data/log/data.txt /opt/ # 查看文件内容(大文件不要用,容易刷屏) hdfs dfs -cat /data/log/data.txt # 查看文件末尾部分内容 hdfs dfs -tail /data/log/data.txt # 删除文件或目录 hdfs dfs -rm -r /data/log # 查看文件块信息和副本位置 hdfs fsck /data/log/data.txt -files -blocks -locations这里特别想提一下fsck命令,很多学HDFS的人都忽略它,但我认为它是最能体现“分布式存储”特性的命令。执行它会告诉你每个文件被切成了几个块、每个块在哪台机器上、副本是否齐全。想象一下,你亲手写个文件传上去,然后用这个命令看到数据块被分布在不同节点上,那种对整个系统的理解会瞬间清晰起来。
4.2 一个完整的日志入库实操
光罗列命令没意思,我带你走一遍实际工作中很常见的“日志数据入库”流程。假设本地有一个应用日志文件app.log,大小约300MB,需要上传到HDFS上统一归档。
第一步,创建数据目录。生产环境里目录一般按业务和时间分层,比如/data/app_log/2025/06/,这样做的好处是后面用Hive建表做分区查询时特别方便。
hdfs dfs -mkdir -p /data/app_log/2025/06第二步,上传文件:
hdfs dfs -put /opt/app.log /data/app_log/2025/06/如果文件比较大,可以加上-D dfs.replication=2临时指定副本数,但这是生产环境才需要考虑的调整,学习阶段保持默认就行。
第三步,检查上传结果:
# 查看文件大小是否与本地一致 hdfs dfs -du -h /data/app_log/2025/06/ # 查看数据块分布 hdfs fsck /data/app_log/2025/06/app.log -files -blocks -locations这个流程练完,你至少应该弄清三件事:文件在HDFS里的真实存储方式、目录结构对后续数据处理的影响、以及怎么验证数据安全地落到了集群里。不要把这个当任务,把它当一次“系统体检”,你会发现命令行没什么神秘的。
4.3 运维视角:巡检时我必看的几个命令
Day6虽然不要求你立刻去管理集群,但有几个运维命令我强烈建议提前接触。以后你一定会感谢自己当初养成了看它们的好习惯。
# 查看HDFS整体健康状态(是否处于安全模式、块数量是否健康) hdfs dfsadmin -report # 查看NameNode的Web UI保存的日志,排查启动和读写报错 tail -100 /opt/hadoop/logs/hadoop-hadoop-namenode.log # 查看DataNode日志 tail -100 /opt/hadoop/logs/hadoop-hadoop-datanode.log我见过太多新手,集群起不来只知道在搜索引擎里复制粘贴报错,其实90%的问题都能在日志里直接找到答案。Hadoop的日志写得非常直白,比如“Cannot connect to NameNode”就是端口或网络不通,“Insufficient permissions"就是权限不够,看到英文别慌,大致能看懂就该知道往哪个方向查。
5. Java API开发:用代码操作HDFS的正确姿势
5.1 开发环境准备
光会用Shell命令,在真实项目里是不够的。因为日志数据往往是程序产出的,不可能每次都靠人去敲put命令。你得能写程序,在代码里自动完成上传、下载、读取、删除这些操作。
开发环境建议用IDEA+ Maven管理依赖。新建一个Maven项目,然后引入Hadoop客户端依赖:
<dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-client</artifactId> <version>3.3.6</version> </dependency>同时要确保本机能解析hdfs://localhost:9000这个地址,如果是本地Linux环境,通常直接访问localhost;如果Hadoop跑在虚拟机上而代码在Windows上写,需要额外配置Windows下的Hadoop环境支持。这个坑不少人都踩过,Windows下跑Hadoop客户端缺少winutils.exe时会报错,解决办法是在Windows本地放一份Hadoop的bin目录并配置环境变量。
5.2 核心代码:上传和下载
配置文件和依赖准备好后,核心代码其实不长。首先创建FileSystem对象,它是操作HDFS的入口:
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import java.io.IOException; import java.net.URI; public class HdfsDemo { public static void main(String[] args) throws IOException { Configuration conf = new Configuration(); // 指定HDFS的地址 conf.set("fs.defaultFS", "hdfs://localhost:9000"); // 获取FileSystem实例 FileSystem fs = FileSystem.get(URI.create("hdfs://localhost:9000"), conf, "hadoop"); // 路径对象 Path src = new Path("D:/data/app.log"); Path dst = new Path("/data/app_log/2025/06/app.log"); // 上传文件 fs.copyFromLocalFile(src, dst); System.out.println("上传完成"); // 下载文件 Path local = new Path("D:/data/download.log"); fs.copyToLocalFile(dst, local); System.out.println("下载完成"); fs.close(); } }这段代码的关键点在于:FileSystem是线程安全的,多个线程可以共享一个实例,不必每次都新建;但用完一定要关闭,否则文件句柄和网络连接会一直泄漏。第三个参数hadoop(用户名)也要特别注意,因为HDFS有权限控制,如果当前系统用户名不在HDFS的超级用户列表里,操作可能被拒绝。
5.3 再深入一步:目录遍历与文件状态获取
学会上传下载后,另一个高频需求是遍历目录,批量读取文件信息。比如说你每天有一批日志文件传上来,程序需要自动识别并处理新文件。这时候可以通过递归遍历目录,拿到每个文件的元信息:
import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.LocatedFileStatus; import org.apache.hadoop.fs.RemoteIterator; // 单层目录遍历 FileStatus[] statuses = fs.listStatus(new Path("/data/app_log")); for (FileStatus status : statuses) { System.out.println("路径: " + status.getPath()); System.out.println("大小: " + status.getLen()); System.out.println("是否为目录: " + status.isDirectory()); } // 递归遍历 RemoteIterator<LocatedFileStatus> iterator = fs.listFiles(new Path("/data/app_log"), true); while (iterator.hasNext()) { LocatedFileStatus status = iterator.next(); System.out.println("文件: " + status.getPath() + ", 块大小: " + status.getBlockSize()); }看完这段代码,你应该能理解一个关键点:Java API和Shell命令本质上在做同一件事,只是一个是人敲命令,一个是程序自动执行。真实的大数据平台,前台上点一个“上传文件”按钮,后面就是这些API在干活。
6. 从本地到云端:基于云平台的大数据开发应该怎么学
6.1 传统自建和云上托管到底差在哪
现在的招聘需求和行业发展方向,都越来越往“基于云平台的大数据应用开发”倾斜。很多公司招聘大数据工程师,不再问你会不会手动搭建Hadoop集群,而是问你有没有用过云上的EMR数据湖、对象存储、托管型数仓。所以Day6阶段,很有必要把视角拉高一点,看看云平台到底改变了什么。
传统的方式是自己买服务器、自己搭集群、自己运维。这个过程能让你学到非常多的底层细节,但代价也很明显:硬件成本高、运维人力重、扩缩容麻烦。比如说公司流量涨了,数据量翻倍,你得提前买机器、加节点,流程至少要几天。
云平台的做法是把它封装成服务,你只要在控制台上点几下或者在API里调一个方法,就能创建出一个带NameNode和DataNode的集群。底层仍然是HDFS那套分布式存储架构,但对外提供的是REST API、SDK这些更易于集成的接口。
6.2 对象存储与HDFS:存储计算分离的新思路
基于云平台做大数据,有绕不开的东西:对象存储,比如阿里云的OSS、腾讯云的COS、AWS的S3。
对象存储和HDFS有什么异同?两者都是分布式存储,但设计思路有差异。HDFS的设计偏重大文件、流式读取,它默认文件是切块的、顺序访问性能更好。对象存储则是“键值对”式的存储,每个对象有一个唯一标识,底层实现更注重高可用和跨地域冗余,访问方式也变成了HTTP接口。
云上的大数据架构,越来越流行“存储计算分离”的模式。也就是说,数据存在对象存储里,计算用弹性伸缩的云上集群,按需拉起用完再释放。这就意味着你今天学HDFS时养成的“存储和计算部署在一起”的思维,需要拓展一下,云上的存储和计算是可以各自独立伸缩的。
但底层的原理相通,数据分块存储的思想、副本或冗余机制保数据的思路、元数据管理的方式,几乎都是从HDFS这套架构演化而来的。所以你完全不用焦虑:本地把HDFS搞懂了,云上的EMR、对象存储、托管数仓对你来说只是换了一层皮,核心还是那套分布式存储和分布式计算的功夫。
6.3 给学习者的建议:本地打基础,云端找视野
我的建议是学习阶段不要一上来就花钱开一堆云服务,先用本地的伪分布式把原理打通,该踩的坑都踩一遍。然后,在掌握本地操作之后,找一个云厂商开通一个最基础的EMR集群或者托管Hadoop服务,亲自体验一下在网页上创建集群、在页面上查看节点状态、用平台自带的数据开发IDE跑一个任务。
这样做的意义有两个:第一,你会见识到真实的工业级大数据平台是什么样子,各种监控、告警、资源调度可视化,比本地自己搭的要成熟太多;第二,你会在简历和面试里有一个核心竞争力——既懂底层原理,又懂云上实践。这两者结合起来,比单会一项的人要有说服力得多。
7. 常见问题与排查技巧实录
7.1 端口不通、进程起不来
Day6最常见的报错就是进程启动失败。如果你执行start-dfs.sh后,jps看不到NameNode进程,可以从这几个方向排查:
- 先看日志。日志路径在Hadoop安装目录的
logs下,我上面提到过。启动失败的原因,99%都能在hadoop-hadoop-namenode.log里找到一句明确的报错。 - 检查端口占用。NameNode默认用9000端口,如果被别的进程占用了,启动会直接失败。用
netstat -tlnp | grep 9000查看。 - 检查防火墙。云服务器尤其要注意,安全组规则里要放通9000、9870端口,否则浏览器访问不了Web界面。
7.2 卡在安全模式
HDFS启动后会自动进入安全模式。安全模式下,文件系统只读,不允许写入和修改。这个机制是为了在NameNode启动时,等待DataNode汇报足够的块信息,直到确认数据完整后才退出。
如果你执行上传文件时报错Name node is in safe mode,先别慌。等一会儿是正常的,但如果长时间卡着就要检查了:
# 查看当前安全模式状态 hdfs dfsadmin -safemode get # 手动退出安全模式(仅在确保集群健康时使用) hdfs dfsadmin -safemode leave如果你在伪分布式里把副本数设置成3,安全模式也会迟迟不退出,因为DataNode永远凑不够副本,集群会一直觉得“数据不健康”。这也是我反复强调伪分布式要把副本数改成1的原因。
7.3 磁盘与副本异常
还有一种很典型的坑:跑着跑着DataNode进程挂了,或者本地磁盘满了。检查方法很简单:
# 查看集群状态,看DataNode是否是活的 hdfs dfsadmin -report # 查看磁盘使用情况 df -h如果DataNode显示为“Dead”,大概率是磁盘空间不足或者网络不通。如果副本数不健康,会有Under-replicated block提示,这时候HDFS会自动从其他节点复制数据来补齐副本,前提是还有别的节点有这份数据。所以平时没事多看看dfsadmin -report的输出,数据副本指数、节点存活状态都一目了然。
7.4 操作HDFS时的几个好习惯
最后分享几个我自己用下来觉得特别值得养成的操作习惯。这些不是教科书上写的,都是实操踩坑换来的。
第一,上传大文件时,不要开着终端干等,可以在命令后面加&让它后台运行,或者用nohup结合日志输出做记录。否则某个会话一断,整个上传任务就被中断了。
第二,删除目录前,养成先看再删的习惯。用-ls确认路径没写错,再看一眼文件内容确认没问题再删。生产环境删除数据是不可逆的操作,HDFS没有回收站,删了就真的没了。
第三,顺手用-du -h关注磁盘空间。HDFS的数据是分散在多台机器上的,单看某台机器的df -h不一定能看到全貌,一定要用HDFS自己的命令来查看整个集群的存储使用量。
第四,学会看Web UI。NameNode的9870端口页面里,DataNode列表、集群存储容量、块数、健康状态都有,比我用命令还直观。没事翻翻它,对集群的整体状态会有很直观的感受。
Day6学到这里,你已经不再是那个只会写Java程序的“单机玩家”了。当你亲手把一个文件传到分布式文件系统上,又亲眼看到它在不同节点上的副本分布,再回头看那些云计算平台上的大数据产品,就会觉得它们没那么神秘。我个人的体会是:分布式存储是整个大数据开发的“定盘星”,哪天你觉得Spark、Hive学得晕头转向,随时可以回到HDFS的底层原理来重新锚定自己。接下来你要做的,就是在Day6的基础上,把数据查询和计算分析的工具一个个加进来,这条路会越走越宽的。