我见过太多人学HDFS是这种状态:概念背得滚瓜烂熟,什么“分布式文件系统”“NameNode管理元数据、DataNode存数据”张口就来,结果自己搭一个集群,传个文件就超时,报错日志看半天都找不到方向。这不是态度问题,是学习顺序反了——先塞了一堆名词,却完全没有建立“这套系统到底怎么转起来”的手感。这篇指南想帮你用最短路径补上这份手感,定位就是2024年快速突破HDFS新手期。我会从HDFS要解决的真实问题讲起,把各角色分工拆开讲明白,然后用当前主流的Hadoop 3.x版本带你在本机搭一个能用的HDFS,再把一条上传命令背后的完整流程彻底讲透,最后把新手最常踩的几个坑连同排查过程原样复盘。适合刚接触大数据、准备转数据方向,或者已经能启动集群但总感觉哪里没搞透的同学。
1. 先想明白:HDFS到底是来解决什么问题的
很多教程上来就讲架构,导致新手脑子里只有一张图,没有“动机”。我建议你先忘掉NameNode、DataNode这些词,回到一个最朴素的问题:一台服务器存数据,到底哪里不够用?想清楚了这个,HDFS所有设计你都推得出来。
1.1 单机存储的三道坎
第一道坎是容量。单块磁盘撑死几TB到几十TB,数据量到PB级之后,你不可能靠往一台机器里不断插硬盘解决问题,机器机箱就那么大。就算你愿意买那种超大存储服务器,单台设备的成本也高得离谱,一般团队根本吃不消。
第二道坎是吞吐。单块磁盘的读写带宽是有物理上限的,机械盘大概一两百MB/s,SSD稍好但也架不住多个任务同时跑。大数据场景下动辄要扫描几个TB的数据,按这个速度,一个任务跑几天都算快的。你要是开几个任务并行抢同一块盘,大家一起卡死。
第三道坎是可靠性。磁盘是会坏的,而且坏的时候不跟你打招呼。单机存储等于把所有鸡蛋放在一个篮子里,盘一挂,数据全没。做备份?你可以搞双盘镜像,但那是拿一倍的成本换一份安全感,规模一大根本扛不住。
这三道坎其实也是所有分布式存储系统的出发点:容量不够就横向扩展多台机器,吞吐不够就让数据分散在多个磁盘上并行读,可靠性不够就多放几个副本。
1.2 HDFS给出的答案:把文件系统搬上集群
HDFS的核心思路就是:把一大堆便宜的普通服务器组织起来,对外看起来像一块超大硬盘。怎么做到?靠两个关键设计。
一个是块存储。HDFS把文件切成固定大小的块(默认128MB),每个块独立存在不同的机器上。这个思路其实和你电脑里的文件系统很像,只不过文件系统管理的是4KB块,HDFS管理的是128MB的大块。块大有什么好处?第一,寻址开销小,顺序扫描时更高效;第二,块的数量少,NameNode需要记录的元数据总量就少。
另一个是多副本。每个块默认存3份,而且这3份副本不是随便扔,而是按照一定的策略分布在不同的机架、不同的节点上。这样哪怕一台机器宕机、甚至整个机架断电,数据依然拿得出来。系统发现某个副本丢失,还会自动在其他机器上再补一份,维持副本数不变。
HDFS同时做了个很重要的取舍:它面向**“写一次、读多次”**的批处理模型。文件一经写入,就不允许随机修改,只能整体读、整体删,最多支持追加。这个设计极大简化了分布式一致性问题的处理,代价是放弃了通用文件系统的一部分灵活性。
1.3 别拿HDFS当普通网盘:它的适用边界
我经常在群里看到有人问“为什么我往HDFS里写几十万个小文件,集群越来越卡”。这其实不是操作问题,是用错了场景。HDFS的强项和弱项非常鲜明,我直接列个表给你看:
| 场景 | 是否适合 | 原因 |
|---|---|---|
| 大文件、顺序读写 | 非常适合 | 块大、流水线复制、顺序扫描吞吐极高 |
| 大数据批处理(离线分析) | 非常适合 | 数据本地性和高吞吐天然匹配MapReduce/Spark |
| 海量小文件 | 非常不适合 | 每个文件都要占用NameNode内存,几千万文件直接拖垮元数据 |
| 毫秒级随机读 | 不适合 | 读一个小数据也要走RPC查元数据、定位块 |
| 随机写、频繁修改 | 不支持 | 设计上只有追加写,不提供随机更新 |
| 结构化数据库场景 | 不适合 | 该用HBase、Kudu这类系统 |
一句话总结:HDFS适合“数据很大、一次写入、多次读取、吞吐优先”的场景。你拿它当一个能存几百TB的网盘,或者拿它当数据库用,都会很难受。
2. 从角色分工看懂HDFS骨架:NameNode、DataNode与客户端各管什么
HDFS整个体系里其实只有三个需要你记住的角色:NameNode、DataNode,以及客户端。前两个是常驻进程,第三个是你顺手就能拿起来用的工具。搞懂他们仨的分工,架构图就不用背了。
2.1 NameNode:集群的“大脑”,活最重但手里没数据
NameNode负责维护整个文件系统的元数据。什么是元数据?就是“哪个文件在哪个目录、文件有多少块、每个块分别存在哪些DataNode上”。它相当于整个集群的目录索引。
关键点在于:这份元数据是全量驻留在内存里的。为了做到快速响应,NameNode把整棵目录树、所有文件属性、所有块的位置信息都加载到内存中,启动时加载,运行时动态更新。这个设计也直接导致了HDFS的一条著名瓶颈——NameNode内存多大,文件系统的文件总数上限就大概有多大。后面第5节讲小文件问题还会展开。
NameNode为了保证元数据不丢,每处理一条修改操作,都会先往本地磁盘追加一条日志(edits log),再返回客户端成功。同时它会定期把内存里的元数据全量快照到磁盘(fsimage)。这里有个常被新手误会的角色叫Secondary NameNode,很多人以为它是NameNode的热备,其实不是,它只是定期把edits和fsimage合并、减轻NameNode压力的一个辅助角色,真正的高可用靠的是后面的HA体系。
还有一点最容易忽略:NameNode不存任何文件内容。它只是“知道”每个块在哪,但块的数据本身都在DataNode上。
2.2 DataNode:真正的一线工人
DataNode才是真正存数据的地方。每个块在DataNode本地磁盘上就是一个普通的文件,只不过这个文件被HDFS纳入了块管理。
DataNode要干的活包括:
- 定期向NameNode上报心跳(默认3秒一次),告诉它“我还活着”。
- 同时定期上报所有块的列表(block report),让NameNode知道本机存了哪些块。
- 执行NameNode下发的指令,比如创建块、删除块、把块复制到别的节点。
- 当某个副本丢失时,按NameNode指令重新复制,恢复副本数。
所以DataNode其实是个“指哪打哪”的工人,它的自主决策很少。这也意味着DataNode可以随时增减——新加一台机器,NameNode就会把新副本调度过去;挂掉一台机器,NameNode等心跳超时后,会自动在其他节点上补副本。
2.3 客户端:牵线搭桥的人
客户端不是独立的服务器进程,而是你使用的命令行工具、Java API、或一个正在运行的计算任务。它的核心作用是分头沟通:客户端和NameNode打交道,获取元数据信息;但真正的数据流转,是客户端直接和DataNode进行,不经过NameNode。
为什么数据不经过NameNode?想一下就明白:如果所有读写数据都流经NameNode,那它就是整个集群的带宽瓶颈,一台机器再强也扛不住PB级数据的吞吐。所以文件内容始终在DataNode之间或客户端与DataNode之间直接流动,NameNode只负责“指路”。
2.4 一次读写中三个角色怎么配合
我用文字描述一下写读两个过程,先留个整体印象,第4节再拆透。
写文件时:客户端先请求NameNode创建文件,NameNode检查权限、配额后允许创建。客户端把文件切成块,每写一个块之前,向NameNode申请“这个块该放哪些DataNode”,NameNode返回一组节点列表。客户端直接往第一个DataNode发数据,这个DataNode边存边传给第二个,第二个传给第三个,形成一条流水线。全部写完,客户端再通知NameNode“块写好了”,NameNode提交元数据。
读文件时:客户端请求NameNode打开文件,NameNode返回文件的块列表,以及每个块在哪些DataNode上有副本。客户端挑一个“最近”的DataNode直接读数据,如果读到的块校验失败,就换一个DataNode重读。
整个过程看着不复杂,但每个环节都有大量细节,第4节专门展开。
3. 亲手跑一个能用的HDFS:单机伪分布式的搭建与验证
理论再好,不如自己动手把集群跑起来。这里我推荐新手从伪分布式开始:一台机器上同时启动NameNode、DataNode、SecondaryNameNode三个进程,模拟一个小集群。它和真正多机集群的内部逻辑完全一致,只是省去了跨机器的网络配置和资源调度。
3.1 为什么从伪分布式起步,而不是直接上三台机器
很多教程一上来就让你准备三台虚拟机、配免密登录、改一堆防火墙规则。我见过不少同学卡在这一步——机器还没凑齐,热情已经凉了。伪分布式的好处是:你只需要一台能跑Linux的机器,就能把HDFS的所有核心机制真实体验一遍,包括格式化、块上报、安全模式、副本复制、故障恢复(你可以手动kill掉DataNode进程观察)。
截至2024年,Hadoop 3.x早已是主流,零散看过很多老教程里还在用Hadoop 1.x、2.x的命令行参数,照做很容易踩坑。你直接用Hadoop 3.3.x就好,功能成熟、社区活跃,配套资料也多。
3.2 安装前置清单:版本和系统准备
环境方面,Linux发行版或者macOS都行,Windows下建议用WSL,避免很多脚本兼容问题。我自己是在Ubuntu虚拟机上跑通的,下面以这个为例。
你需要提前确认两件事:
- JDK版本:Hadoop 3.3.x官方支持JDK 8和JDK 11,建议装JDK 8或11,不要用太新的JDK版本去挑战兼容性。跑一下
java -version确认已就绪。 - SSH免密登录:Hadoop的脚本需要远程登录本机来启动DataNode,所以要让当前用户能免密ssh到localhost。执行
ssh-keygen -t rsa生成密钥,再cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys,之后ssh localhost验证一下。
接着去Hadoop官网下载二进制包,不要下载源码包自己编译,那属于给自己找事。下载后解压到某个目录,比如/opt/hadoop,然后配置环境变量:
export HADOOP_HOME=/opt/hadoop export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin3.3 三个核心配置文件里的关键项
HDFS的配置分布在$HADOOP_HOME/etc/hadoop目录下,新手主要改三个文件:core-site.xml、hdfs-site.xml,以及设置Java路径的hadoop-env.sh。
core-site.xml里最核心的是默认文件系统和临时目录:
<configuration> <property> <name>fs.defaultFS</name> <value>hdfs://localhost:9000</value> </property> <property> <name>hadoop.tmp.dir</name> <value>/data/hadoop/tmp</value> </property> </configuration>这里我特别想强调hadoop.tmp.dir。默认值是在/tmp下的,很多系统重启会清理/tmp,一旦被清空,NameNode的元数据就没了,整个集群形同报废。所以务必把这个目录改到你的数据盘上,比如/data/hadoop/tmp,新建目录并赋权。
hdfs-site.xml里要配置NameNode和DataNode各自的存储目录,以及副本数:
<configuration> <property> <name>dfs.namenode.name.dir</name> <value>/data/hadoop/name</value> </property> <property> <name>dfs.datanode.data.dir</name> <value>/data/hadoop/data</value> </property> <property> <name>dfs.replication</name> <value>1</value> </property> </configuration>伪分布式环境下一台机器只有一个DataNode,副本数配1就够,配3不仅浪费空间,还会因为副本永远凑不够而反复触发复制。dfs.replication默认是3,但可以根据实际节点数调整。
3.4 格式化与启动:两个最常翻车的地方
在启动之前要做一次格式化:
hdfs namenode -format格式化会初始化文件系统的元数据,生成一个空的fsimage和集群ID。这里我要敲一下黑板:格式化只在文件系统初次创建时执行一次。之后只要你不乱动,就别再执行它。我见过有人启动失败,顺手又格式化了一遍,结果Format的那台NameNode生成了新的集群ID,老DataNode上报时发现集群ID不匹配,直接拒绝连接——这个坑在第6节详细讲。
格式化完成后,用一条命令启动所有进程:
start-dfs.sh然后立刻用jps检查进程:
jps正常情况下你会看到NameNode、DataNode和SecondaryNameNode三个Java进程。如果缺了DataNode,别急,去看日志。
启动后先别急着传文件,打开NameNode的Web界面(默认http://localhost:9870),或者用命令行看集群状态:
hdfs dfsadmin -report这条命令会显示当前有多少活的DataNode、每台节点的存储容量和已用空间。如果显示Live datanodes (1),说明集群健康。
启动初期集群会处于安全模式,这是NameNode在等DataNode上报块信息,属于正常现象,等块上报达到阈值后会自动退出。判断当前是否还在安全模式,可以用:
hdfs dfsadmin -safemode get如果显示Safe mode is OFF,就可以正式使用了。
3.5 用命令行建立“手感”:亲身感受块切割
集群跑起来之后,我强烈建议你做两个小实验。
第一个,建目录、传文件、读文件,把最常用的命令过一遍:
hdfs dfs -mkdir -p /user/test echo "hello hdfs" > hello.txt hdfs dfs -put hello.txt /user/test/ hdfs dfs -ls /user/test/ hdfs dfs -cat /user/test/hello.txt hdfs dfs -get /user/test/hello.txt ./hello_download.txt hdfs dfs -rm /user/test/hello.txt这些命令和Linux命令高度相似,但底层走的是HDFS的RPC接口,多敲几遍就没问题了。
第二个实验才是重点:感受块的切割。我们先用dd生成一个300MB的文件:
dd if=/dev/zero of=bigfile.bin bs=1M count=300 hdfs dfs -put bigfile.bin /user/test/ hdfs fsck /user/test/bigfile.bin -files -blocksfsck命令会输出文件被切成几个块、每个块的副本在哪些节点上。这时候你会发现,300MB的文件被分成了3个块(默认128MB一个,第三个块44MB),而且每个块后面跟着一堆节点地址。这种直观感受是单纯看文档永远得不到的——你亲手验证了“块存储”这件事是真的在发生。
随着后续学习,你还可以尝试把DataNode进程kill掉,再观察副本复制动作,体会一下故障自愈。
4. 一条命令背后的完整旅程:HDFS写入与读取的内部流程拆解
用命令行上传文件很简单,但这一条命令背后藏着HDFS最核心的设计智慧。把这部分看透,你对HDFS的认知就能超过大多数只背概念的人。
4.1 写入流程:申请、分块、流水线复制与确认
假设你要上传一个300MB的文件,客户端做的事情并不是一次性把整个文件丢给NameNode,而是拆成多个块逐个处理。
第一步,客户端调用create接口,向NameNode申请创建文件。NameNode会做权限检查、路径是否已存在、配额是否够用。通过后,它在内存目录树中创建文件入口,但此时还没有任何块信息。注意,这一步NameNode会先写一条edits日志,保证元数据操作可恢复。
第二步,客户端开始逐块写入。每个块默认128MB,客户端按64KB或更小的数据包组织数据,并在每个数据包中附带校验信息。正式发送数据之前,客户端会向NameNode为当前块申请DataNode列表。如果副本数是3,NameNode会按照机架感知策略返回一组节点。
第三步,真正的数据传输。客户端把数据流直接发给列表中的第一个DataNode。第一个DataNode收到一部分数据后,一边落盘,一边把同样的数据转发给第二个DataNode,第二个再转发给第三个。这就是所谓的流水线复制,数据在节点间顺着管道路由,而不是客户端分别发给三个人——省了不管多少副本,客户端只需传一次。
第四步,逐级确认。每个DataNode写完一块数据后,会向上一个节点返回确认信息,最后回到客户端。客户端确认该块写入成功后,再向NameNode汇报“这个块写完了”,NameNode提交块信息到元数据。然后客户端继续写下一个块,循环直到整个文件写完。文件所有块提交后,NameNode关闭文件,写操作才算彻底成功。
这里有个性能要点:流水线的速度取决于最慢的那个DataNode。所以生产集群里,节点磁盘性能过于参差会导致整个写入被拖慢,这也是为什么同样规模的集群,有的写入快、有的写入慢。
4.2 读取流程:元数据先行,数据就近拿
读文件比写文件简单得多,因为它不改变任何状态,没有大量的确认和提交环节。
客户端调用open接口,NameNode返回文件的元数据信息——文件分成几块、每个块在哪些DataNode上有副本。客户端拿到这个“地图”后,对每个块挑选一个合适的DataNode去读。
“合适”是怎么定义的?HDFS会按网络距离对副本位置排序。客户端优先访问本机上的副本,如果没有,就看同机架的节点,最后才考虑跨机架的节点。这就是数据本地性思想,大数据计算框架(比如MapReduce、Spark)非常依赖这一特性:把计算任务调度到数据所在的节点,省掉大量网络传输。
读到数据时,客户端会校验校验和。如果发现某个块的副本损坏,它不会傻等,而是换一个DataNode上的副本重新读取,同时报告NameNode,NameNode会安排复制一个新的完好副本,并最终删除损坏的副本。
相比之下,读取比写入快的一个重要原因是:读任务不产生写日志、不被副本流水线拖累,只需要“找准位置,拉数据”。这也是为什么HDFS特别适合做数据分析——一次写入,海量读取,读取性能被优化到了极致。
4.3 为什么元数据与数据必须分离:一个有点反直觉的设计
很多人刚接触HDFS时会觉得奇怪:NameNode既然是“大脑”,为什么不顺便把数据也存了?省得客户端还要分头找DataNode。这里面其实是个经过权衡的架构决策。
如果把元数据与数据放在同一台节点上,那么扩容新节点时,不仅要迁移数据,还要牵涉元数据重新分布,复杂度会呈指数级上升。而HDFS把两者彻底分离之后,业务侧需要存的数据大多了,直接加DataNode就能横向扩容,完全不影响NameNode;反之,如果文件系统里的目录结构变得复杂了,可以单独扩展NameNode规格或者上联邦机制。
更深一层是性能考虑。元数据要求的是高并发、低延迟响应,全部放在NameNode内存里,能做到微秒级查询;而文件数据要求的是高吞吐、顺序读写,放在普通磁盘上。这两个需求完全不同,强行揉在一起,两边都做不好。
这个设计也造成了一个潜在问题:元数据全在内存,意味着内存容量成为整个集群文件数量的天花板。每多一个文件或目录,NameNode内存就要多占一部分空间。这也是第5节要重点说的小文件问题的根源。
5. 新手必须认识的几个配置参数:理解比背参数更重要
HDFS的配置项很多,但新手不需要面面俱到。先把这几个高频参数的原理弄明白,日常使用和排查问题基本就够用了。
5.1 块大小和副本数:所有空间账都要从这里算
块大小默认是128MB,这是全局参数dfs.blocksize决定的。为什么不设成1MB或者1GB?如果块太小,一个超大文件会被切成几百万个块,NameNode要管理的信息量巨大;如果块太大,比如1GB,计算任务拿一个块来处理,内存压力大、并行度又不高。128MB是折中后的经验值。
副本数默认是3,对应dfs.replication。副本数的成本很好算:副本数等于几,你实际消耗的存储空间就是原始文件大小的几倍。1GB文件存3副本,物理占用3GB。所以做容量规划时,第一件事就是用“文件总量 × 副本数”来估算集群实际需要多少存储。
副本数也影响着数据的容错能力,但文件级副本数可以在上传时临时指定,比如这条命令把某个文件单独设成2个副本:
hdfs dfs -Ddfs.replication=2 -put bigfile.bin /user/test/不过上传后如果副本数不满足,HDFS会尝试异步补副本,所以临时指定的副本数如果太小,有时候会看到副本不足的告警,不用太惊慌。
5.2 心跳、超时与块上报:节点“死亡”是怎么判定的
DataNode默认每3秒给NameNode发一次心跳(dfs.heartbeat.interval),告诉对方自己还活着。这个机制很像手机上的“已读回执”,NameNode通过心跳来维护一张活跃节点列表。
如果某个DataNode超过一定时间没有心跳,NameNode就把它标记为dead。这个超时时间不是简单写死的,经典的计算经验是:
超时时间约等于
2 × dfs.namenode.heartbeat.recheck-interval + 10 × dfs.heartbeat.interval。
默认heartbeat.recheck-interval是5分钟,按公式算下来,一个DataNode大约10分多钟没有心跳就会被判死。一旦节点被判死,NameNode会将其上的所有块标记为副本缺失,然后安排在其他节点上重新生成副本,维持整个集群的副本数目标。
理解了这个逻辑,你排查“某台机器挂了之后集群在大量复制数据”的现象时就会很淡定:这是系统在做自愈,不是出了什么神秘故障。
5.3 安全模式:启动时你在等待什么
每次NameNode启动,都会先进入安全模式。在安全模式下,文件系统对外表现为“只读”,不允许创建、删除文件,不响应写请求。你可以理解成一台电脑开机时先做磁盘自检,没做完整之前不让你轻易动文件。
NameNode在等什么呢?等DataNode把各自存有的块列表上报上来,然后NameNode核对元数据中记录的每个块,看有多少块满足“最小副本数”要求。当满足条件的块数达到阈值——默认99.9%(dfs.namenode.safemode.threshold-pct),安全模式才会自动退出。
万一启动后安全模式一直不退,大概率就是DataNode上报的块数不够,常见原因包括节点宕机、磁盘损坏、或者集群ID不一致导致DataNode根本连不上NameNode,这个我们在第6节用真实案例展开。
5.4 小文件问题:最容易被低估的“隐藏炸弹”
HDFS处理大文件是强项,但如果你往里面灌大量小文件,比如几十万张几KB的图片,整个集群的性能会急剧恶化。原因还是那块内存天花板。
业内粗略的估算公式是:每个文件(inode)大约占NameNode内存150字节,每个数据块大约是150字节。看数字好像没多少,但你算一下:1000万个文件,每个至少1个块,那就是2000万条元数据记录,动辄几个GB内存,而且每一条文件的RPC请求都要NameNode响应,GC压力和并发压力一起来,整个集群都被拖慢。
所以生产环境遇到小文件,标准应对方式是在写入前做合并:要么把小文件合并成SequenceFile、ORC这类大文件格式,要么用Hadoop Archive(HAR)把多个小文件打包成一个归档文件。如果小文件来自实时的流式数据,更合适的方案是用对象存储或专门的消息系统,而不是直接怼进HDFS。
提示:我见过不止一个团队因为“HDFS比较简单,先存着以后再说”的心态,把几百万个小文件塞进来,最后不得不花好几周做数据迁移。这个成本比一开始就规划文件格式高得多。
6. 我在HDFS上踩过的四个坑:根因分析与解决记录
这部分是我自己搭环境、维护集群时真正踩过的坑,每一个都代表了新手的高频问题。我按“现象→排查→修复”的方式写,你照着这个思路走,能省不少弯路。
6.1 格式化目录还在/tmp,重启一夜回到解放前
现象:某天下班关机,第二天启动集群,NameNode进程起不来,日志报元数据加载失败。
根因:当初搭建时没有设置hadoop.tmp.dir,走了默认的/tmp/hadoop-用户名目录。操作系统在关机重启时清理了/tmp,NameNode的edits和fsimage全没了。这等于电脑硬盘被格式化,文件系统自然起不来。
解决:把hadoop.tmp.dir指到独立的数据盘目录,例如/data/hadoop/tmp;同时检查dfs.namenode.name.dir是否指向了同一个数据盘下的子目录。配置改好后重新格式化、重新启动。
这件事给我的教训是:凡是要长期运行的组件,默认用户目录和临时目录都要第一时间改掉。老教程里让你“就这么配”,那是教学环境,生产环境照着做是要出事儿的。
6.2 安全模式卡了一下午,结果是磁盘被日志塞满
现象:集群重启后,NameNode Web界面一直显示安全模式,safemode get返回ON,等半小时也不退出。
排错链路:
- 执行
hdfs dfsadmin -report,发现Live DataNode数量不为0,节点在线。 - 继续检查NameNode日志,没有报元数据损坏。
- 再用
df -h看磁盘,发现NameNode所在节点的磁盘使用率达到100%。 - 顺着日志目录一看,HDFS自身日志和系统日志已经把数据盘塞满了,导致NameNode无法正常写edits日志,也不能完成块上报的元数据持久化。
解决:清理磁盘空间,腾出余量后,重启NameNode,安全模式很快自动退出。这次之后我养成了一个习惯:任何组件卡住的时候,先看磁盘,再看CPU,然后才是日志——三者成本依次递增,但磁盘问题最常被忽略。
6.3 把网页端口当成RPC端口,客户端傻傻连不上
现象:一个同学写了Java代码连接HDFS,地址填hdfs://localhost:9870,运行时报Connection refused。
根因:新手看到NameNode页面在9870端口打开,就以为这是HDFS的连接入口。实际上9870是NameNode的Web UI端口,用于浏览器访问和监控;客户端RPC通信用的是fs.defaultFS里配置的那个端口,比如9000或8020。拿网页端口去给客户端连,就像拿门牌号去找餐厅的取餐窗口,当然找不到。
常见的端口对照我放在这里,以后别再搞混:
| 功能 | Hadoop 3.x常见端口 | Hadoop 2.x常见端口 |
|---|---|---|
| NameNode RPC端口 | 9000或8020 | 9000或8020 |
| NameNode Web UI | 9870 | 50070 |
| Secondary NameNode Web UI | 9868 | 50090 |
| DataNode数据通信端口 | 9866 | 50010 |
解决:客户端连接的地址一定要用fs.defaultFS里的RPC端口。排查时可以用netstat -anp | grep 9000或telnet验证端口是否在监听。
6.4 忘了配置DataNode数据目录,所有块都堆到系统盘
现象:集群跑了几天,突然发现NameNode所在服务器的系统盘满了,各种进程开始报错。
根因:搭建时只配置了NameNode的元数据目录,没有显式指定dfs.datanode.data.dir。于是DataNode把所有数据块都写到了hadoop.tmp.dir的默认子目录下——又回到那个被忽视的默认路径,而且它和系统盘在同一个分区。块越写越多,系统盘被堆爆。
解决:将dfs.datanode.data.dir明确指向独立的大容量数据盘,并把它和dfs.namenode.name.dir分开放置。注意,生产环境里NameNode的元数据建议放在专门的可靠磁盘上,DataNode的数据目录则要充分利用多块数据盘,用逗号分隔多个目录,让块分散到不同磁盘。
提示:改完DataNode的数据目录后,旧数据不会自动迁移,需要自己手动把块文件搬到新目录,或者干脆重新格式化重建集群。所以这个配置最好在搭建第一天就做对,后面再改成本很高。
如果让我重新学一遍HDFS,我会把顺序定成:先在伪分布式上把命令跑熟,再故意模拟节点宕机、观察安全模式和副本自愈,最后才去啃调优和源码。所有绕开实操的“速成”都是假速成,HDFS最好的老师就是你自己那台机器上不停翻滚的日志。看完这篇,建议你立刻动手搭一个,踩几个真的坑,比什么都管用。