1. 为什么今天还要聊Hadoop
大数据这个词被喊了十几年,技术栈换了一茬又一茬,流计算的框架从Storm换到Flink,存储引擎从HDFS扩展到对象存储和数据湖,调度器从YARN一路卷到K8s。但如果你现在去翻招聘要求,或者去问任何一个做数据平台超过五年的老手,Hadoop依然是绕不开的底座。原因很简单:它定义了一套分布式系统处理海量数据的标准范式,后面所有的演进,本质上都是在这套范式上做优化和替换。
我见过不少团队,一上来就追新,直接上Flink加Iceberg,结果连最基础的“数据怎么分片、任务怎么调度、失败怎么重试”都没想清楚,最后系统跑得一地鸡毛。反过来,那些把Hadoop这套东西吃透的团队,换任何新框架都是平滑迁移,因为底层逻辑是通的。
这篇内容适合三类人看:第一类是刚接触大数据、需要建立整体认知的开发者;第二类是有一定经验、但一直没系统梳理过Hadoop内部机制的后端或数据工程师;第三类是需要做技术选型、想搞清楚Hadoop到底还能不能用、该怎么用的技术负责人。我会从设计思路、核心组件、实操部署、问题排查几个维度,把Hadoop这套东西拆开讲清楚,尽量说人话,少堆术语。
2. Hadoop整体架构与设计思路拆解
2.1 核心要解决的问题:一台机器扛不住怎么办
Hadoop诞生的起点非常朴素:数据量涨得比硬盘快,单机处理不过来了。那最直接的想法就是把数据分散到多台机器上,把计算也分散到多台机器上。但一旦涉及多机,问题就成倍增加:数据怎么切分、怎么保证不丢、机器挂了怎么办、多台机器怎么协调、网络传输会不会成为瓶颈。
Hadoop的设计哲学可以概括成三句话:移动计算比移动数据便宜、假设硬件一定会坏、用软件层面的冗余换硬件的廉价。这三句话听起来简单,但后面所有的架构决策都是从这里推导出来的。
“移动计算比移动数据便宜”直接决定了Hadoop把计算任务调度到数据所在的节点上执行,而不是把数据拉到计算节点。你想想,一个1TB的数据集,如果每次计算都要通过网络传输,光网络带宽就是灾难。而把计算逻辑(通常只有几KB的jar包)发到数据节点,本地读取磁盘,效率完全不是一个量级。
“假设硬件一定会坏”意味着Hadoop从设计之初就不依赖单机的可靠性。它默认节点会宕机、磁盘会损坏、网络会抖动,所以所有数据默认存三份,所有任务都可以重试。这个思路和传统数据库追求单机高可靠完全不同,是用数量换质量。
“用软件冗余换廉价硬件”是这套体系能普及的根本原因。你不需要买昂贵的存储阵列和小型机,用一堆普通服务器就能搭出一个能处理PB级数据的集群。成本降下来了,大数据才真正从实验室走进了普通公司。
2.2 三大核心组件的分工与协作
Hadoop的核心组件其实就三个:HDFS负责存、MapReduce负责算、YARN负责调度。这三个东西各司其职,又紧密配合。
HDFS是分布式文件系统,它把一个大文件切成固定大小的块(默认128MB),每个块存多份(默认3份),分散在不同节点上。这样做的好处是:单个文件可以比任何一台机器的硬盘都大,读取时可以并行从多个节点拉取,而且任何一台机器挂了都不影响数据完整性。
MapReduce是计算框架,它把一个大计算任务拆成两个阶段:Map阶段做局部处理,Reduce阶段做全局汇总。比如你要统计一个100GB文本里每个单词出现的次数,Map阶段每台机器各自统计自己本地数据的词频,Reduce阶段把所有机器的局部结果合并成最终结果。这个模型虽然简单,但能覆盖大量批处理场景。
YARN是资源调度器,它负责管理整个集群的CPU和内存资源,决定哪个任务先跑、哪个任务分多少资源。没有YARN之前,Hadoop集群只能跑MapReduce一种任务,有了YARN之后,Spark、Flink、Tez都可以跑在同一个集群上,资源利用率大幅提升。
这三个组件的协作流程大致是这样的:客户端提交一个计算任务给YARN,YARN分配一个容器启动ApplicationMaster,ApplicationMaster向YARN申请更多容器来跑Map和Reduce任务,Map任务优先被调度到数据所在的节点,Reduce任务拉取Map的输出进行汇总,最终结果写回HDFS。
2.3 为什么是这套架构而不是别的
你可能会问,为什么HDFS要切块而不是直接存整个文件?为什么块大小是128MB而不是1MB或者1GB?为什么默认存三份而不是两份或者五份?
块大小128MB这个数字不是拍脑袋定的。HDFS的块大小需要平衡两个因素:寻址开销和并行度。块太小,NameNode需要管理的块数量暴增,内存压力大,而且每次读取都要频繁寻址;块太大,并行度不够,一个块只能由一个Map任务处理,如果块是1GB,那Map任务数量就太少,集群利用率上不去。128MB是在当时硬件条件下算出来的一个平衡点,机械硬盘顺序读取速度大约100MB/s,128MB大概读1.3秒,寻址时间占比可以接受。
副本数默认3份也是权衡的结果。2份的话,如果同时坏两块的概率虽然低,但在大规模集群里并非不可能;3份在可靠性和存储成本之间取得了比较好的平衡。当然,对于冷数据或者可以重新计算的数据,完全可以调成2份甚至1份,这个后面会讲。
YARN的出现其实是一个架构上的分水岭。在YARN之前,Hadoop集群的节点数量上限受限于JobTracker的单点瓶颈,大概只能管4000个节点左右。YARN把资源管理拆成ResourceManager和ApplicationMaster两层,ResourceManager只做全局资源分配,具体的任务调度交给每个应用自己的ApplicationMaster,这样集群规模可以扩展到上万个节点。
3. HDFS核心机制与实操要点
3.1 NameNode和DataNode到底在干什么
HDFS是一个典型的主从架构,NameNode是主,DataNode是从。但NameNode并不存实际数据,它只存元数据:文件有哪些块、每个块存在哪些DataNode上、文件的目录结构、权限信息等等。实际的数据块全部存在DataNode的本地磁盘上。
NameNode的元数据全部放在内存里,这是它能快速响应客户端请求的关键。但内存的东西断电就没了,所以它需要把元数据变更持久化到磁盘。这里有个关键设计:EditLog加FsImage。EditLog记录所有写操作,FsImage是某个时间点的元数据快照。NameNode启动时先加载FsImage,再重放EditLog,就能恢复出完整的内存状态。
DataNode负责实际的数据存储和读取。它定期向NameNode发送心跳(默认3秒一次),汇报自己还活着以及自己存了哪些块。如果NameNode超过一定时间(默认10.5分钟)没收到某个DataNode的心跳,就认为它挂了,然后启动副本复制,把该节点上的块在其他节点上再存一份。
这里有个实操中很容易踩的坑:NameNode的内存配置。每个块大约占150字节的元数据,如果你有1亿个块,光元数据就要15GB内存。很多团队一开始没注意,集群跑了一段时间发现NameNode频繁Full GC,就是因为块数量涨太快,内存不够了。解决办法要么加内存,要么合并小文件,要么启用HDFS Federation把元数据分散到多个NameNode上。
3.2 数据写入和读取的完整流程
先看写入流程。客户端要写一个文件到HDFS,大致经过这些步骤:
- 客户端向NameNode发起创建文件请求,NameNode在元数据里创建文件条目,但此时不分配任何块。
- 客户端开始写数据,数据先缓存在本地,当积累到一个块大小(128MB)时,向NameNode请求分配块和DataNode列表。
- NameNode返回一组DataNode(默认3个),客户端直接向第一个DataNode写数据,第一个DataNode再向第二个写,第二个向第三个写,形成一条管道。
- 数据在管道里逐级传输,每传一级都会做校验。如果某个DataNode写失败,管道会重建,跳过失败的节点继续写。
- 所有块写完后,客户端通知NameNode关闭文件,写入完成。
读取流程相对简单:客户端向NameNode请求文件块的位置信息,NameNode返回每个块所在的DataNode列表(按网络距离排序),客户端直接向最近的DataNode读取数据。如果读取失败,客户端会尝试下一个副本。
注意:HDFS适合一次写入多次读取的场景,不支持随机写。如果你需要频繁修改文件内容,HDFS会让你很痛苦。这是它和传统文件系统最大的区别。
3.3 副本策略与机架感知
默认情况下,HDFS的副本放置策略是这样的:第一个副本放在客户端所在的节点(如果客户端在集群内),第二个副本放在不同机架的某个节点,第三个副本放在第二个副本所在机架的另一个节点。
这个策略的核心目的是兼顾可靠性和带宽。放在不同机架是为了防止整个机架断电导致数据全丢;但跨机架传输带宽有限,所以第三个副本放在同机架,读取时可以优先从同机架读,减少跨机架流量。
机架感知需要集群配置一个脚本或者Java类来告诉Hadoop每个节点的机架信息。如果没配置,Hadoop默认所有节点在同一个机架,副本策略就退化成随机选节点,可靠性会打折扣。很多小集群觉得机架感知无所谓,但一旦规模上去,这个配置是必须的。
副本数是可以按文件设置的。比如临时中间结果可以设成1份,重要业务数据设成3份,归档数据设成2份。命令很简单:
hdfs dfs -setrep -w 2 /path/to/file这个命令会把指定文件的副本数调整为2,-w表示等待副本调整完成再返回。
3.4 小文件问题与合并方案
HDFS最怕小文件。每个文件、每个块在NameNode内存里都要占一份元数据,一个1KB的文件和一个128MB的文件占的元数据差不多。如果你有1000万个1KB的小文件,NameNode内存直接爆炸,而且MapReduce处理时每个小文件都要起一个Map任务,任务启动开销比处理数据本身还大。
解决小文件问题有几种常见方案:
- 写入时合并:在数据进入HDFS之前,用Flume或者自己写程序把多个小文件合并成一个大文件再写入。
- HAR归档:Hadoop Archive可以把多个小文件打包成一个HAR文件,减少NameNode元数据压力,但读取时需要解包,有一定开销。
- CombineFileInputFormat:在MapReduce层面,用CombineFileInputFormat把多个小文件合并到一个Map任务里处理,减少任务数量。
- 定期合并:跑定时任务,把历史小文件合并成大文件,然后删除原文件。
我个人的经验是,在数据入口做合并是最有效的。等小文件已经写进HDFS再去合并,成本和风险都高很多。入口处用Flume的hdfs.rollSize和hdfs.rollInterval参数控制文件滚动大小,尽量让每个文件接近块大小。
4. MapReduce与YARN运行机制
4.1 MapReduce的编程模型和执行流程
MapReduce的编程模型非常克制,只有两个核心接口:Map和Reduce。Map负责把输入数据转换成键值对,Reduce负责把相同键的值合并。
以一个日志分析为例:输入是Nginx日志,每行一条记录,你想统计每个URL的访问次数。Map阶段读每一行,解析出URL作为key,1作为value,输出<url, 1>。Reduce阶段收到某个URL的所有value,求和,输出<url, total_count>。
这个模型之所以强大,是因为它把并行计算的复杂性全部封装了。你只需要写Map和Reduce的逻辑,至于数据怎么切分、任务怎么调度、失败怎么重试、中间结果怎么传输,框架全帮你搞定。
但MapReduce也有明显的短板:中间结果要落盘。Map的输出会写到本地磁盘,Reduce再从各个Map节点拉取数据,这个过程涉及大量磁盘IO和网络传输。对于迭代计算(比如机器学习训练),每一轮迭代都要读写磁盘,效率很低。这也是后来Spark能取代MapReduce成为主流计算引擎的核心原因——Spark把中间结果放在内存里。
4.2 YARN的资源调度模型
YARN把集群资源抽象成Container,每个Container包含一定量的CPU和内存。ResourceManager是全局资源管理者,NodeManager是每个节点上的代理,负责启动和监控Container。
当一个应用提交到YARN时,流程是这样的:
- 客户端向ResourceManager申请启动ApplicationMaster。
- ResourceManager分配一个Container,NodeManager在其中启动ApplicationMaster。
- ApplicationMaster向ResourceManager注册自己,然后申请运行任务所需的Container。
- ResourceManager把Container分配给ApplicationMaster,ApplicationMaster通知对应的NodeManager启动任务。
- 任务运行过程中,ApplicationMaster监控任务状态,失败则重新申请Container重试。
- 所有任务完成后,ApplicationMaster向ResourceManager注销,整个应用结束。
YARN支持多种调度器:FIFO、Capacity、Fair。FIFO就是先来先服务,简单但容易造成大任务阻塞小任务。Capacity调度器把集群资源划分成多个队列,每个队列有容量保证,适合多租户场景。Fair调度器动态调整资源分配,让所有应用公平地共享资源。生产环境用得最多的是Capacity和Fair,具体选哪个看你的业务场景。
4.3 一个完整的MapReduce任务提交实操
假设你已经搭好了一个Hadoop集群,现在要提交一个WordCount任务。步骤如下:
第一步,准备输入数据:
hdfs dfs -mkdir -p /input/wordcount hdfs dfs -put localfile.txt /input/wordcount/第二步,提交任务(假设你已经打好了jar包):
hadoop jar wordcount.jar com.example.WordCount /input/wordcount /output/wordcount第三步,查看任务状态:
yarn application -list第四步,查看输出结果:
hdfs dfs -cat /output/wordcount/part-r-00000这里有几个实操细节值得注意。输出目录必须不存在,否则任务会直接报错退出,这是为了防止误覆盖已有数据。Reduce任务数量默认是1,如果数据量大,需要手动设置mapreduce.job.reduces参数,否则所有数据都挤到一个Reduce里,成为瓶颈。Map任务数量由输入分片决定,每个分片一个Map任务,分片大小默认等于块大小。
提示:提交任务时可以通过
-D参数覆盖配置,比如-D mapreduce.job.reduces=10,这样不用改配置文件就能调整任务参数。
4.4 任务失败与重试机制
MapReduce对失败的容忍度很高。Map任务失败后,ApplicationMaster会在另一个节点上重新调度该任务。Reduce任务失败后,同样会重新调度。但如果一个任务连续失败超过一定次数(默认4次),整个作业会被标记为失败。
任务失败的原因通常有几类:代码bug(比如空指针)、数据问题(比如格式不符合预期)、资源不足(比如内存溢出)、节点故障。排查时首先要看任务日志,YARN提供了日志聚合功能,可以通过yarn logs -applicationId <appId>查看完整日志。
有一个很容易被忽略的点:Map任务的重试不会重新读取输入数据,它只是重新执行Map逻辑。但如果Map任务已经产生了一些输出,重试时会覆盖之前的输出。Reduce任务重试时,会重新拉取所有Map的输出,所以如果Map输出很大,Reduce重试的成本会很高。
5. 集群部署与配置实操
5.1 部署模式选择:单机、伪分布式、完全分布式
Hadoop支持三种部署模式:
- 单机模式:所有组件跑在一个JVM里,用本地文件系统,不用HDFS。适合开发和调试MapReduce程序逻辑。
- 伪分布式模式:所有组件跑在一台机器上,但用HDFS和YARN。适合学习Hadoop的完整运行机制,资源消耗不大。
- 完全分布式模式:多台机器组成集群,NameNode、ResourceManager、DataNode、NodeManager分布在不同节点。生产环境必须用这种模式。
如果你只是想学习,伪分布式足够了。但要注意,伪分布式的配置和完全分布式基本一致,只是节点数量不同。所以你在伪分布式上踩过的配置坑,在完全分布式上一样会遇到。
5.2 核心配置文件详解
Hadoop的配置主要集中在几个XML文件里:
core-site.xml:定义HDFS的默认文件系统和临时目录。
<configuration> <property> <name>fs.defaultFS</name> <value>hdfs://namenode-host:9000</value> </property> <property> <name>hadoop.tmp.dir</name> <value>/data/hadoop/tmp</value> </property> </configuration>hdfs-site.xml:定义副本数、NameNode和DataNode的数据目录。
<configuration> <property> <name>dfs.replication</name> <value>3</value> </property> <property> <name>dfs.namenode.name.dir</name> <value>/data/hadoop/namenode</value> </property> <property> <name>dfs.datanode.data.dir</name> <value>/data/hadoop/datanode</value> </property> </configuration>yarn-site.xml:定义ResourceManager和NodeManager的配置。
<configuration> <property> <name>yarn.resourcemanager.hostname</name> <value>resourcemanager-host</value> </property> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> <property> <name>yarn.nodemanager.resource.memory-mb</name> <value>16384</value> </property> </configuration>mapred-site.xml:指定MapReduce运行在YARN上。
<configuration> <property> <name>mapreduce.framework.name</name> <value>yarn</value> </property> </configuration>这里有个关键参数yarn.nodemanager.resource.memory-mb,它决定了每个NodeManager能分配给Container的总内存。这个值要根据机器的实际物理内存来设,一般留出20%给操作系统和其他进程。比如一台64GB内存的机器,可以设成50GB左右。
5.3 集群启动与验证
配置完成后,首次启动需要格式化NameNode:
hdfs namenode -format这个命令会创建NameNode的元数据目录。注意,格式化只能执行一次,重复格式化会导致DataNode的clusterID和NameNode不一致,DataNode会拒绝启动。如果不小心格式化了两次,需要把DataNode的数据目录也清空重新格式化。
启动HDFS:
start-dfs.sh启动YARN:
start-yarn.sh验证集群状态:
hdfs dfsadmin -report yarn node -listhdfs dfsadmin -report会显示所有DataNode的状态、容量、已用空间等信息。yarn node -list会显示所有NodeManager的状态和资源使用情况。
5.4 生产环境的关键配置调优
生产环境和实验环境最大的区别是稳定性和性能。以下参数是我在实际项目中总结出来的关键调优点:
| 参数 | 默认值 | 建议值 | 说明 |
|---|---|---|---|
| dfs.namenode.handler.count | 10 | 100-200 | NameNode处理RPC请求的线程数,集群大时需调高 |
| dfs.datanode.max.transfer.threads | 4096 | 8192 | DataNode处理数据传输的最大线程数 |
| yarn.nodemanager.resource.cpu-vcores | 8 | 实际核数 | NodeManager可分配的虚拟CPU核数 |
| yarn.scheduler.maximum-allocation-mb | 8192 | 32768 | 单个Container最大内存,跑大任务时需调高 |
| mapreduce.map.memory.mb | 1024 | 2048-4096 | Map任务内存,根据任务实际需求调整 |
| mapreduce.reduce.memory.mb | 1024 | 4096-8192 | Reduce任务内存,通常比Map大 |
这些参数没有万能值,需要根据集群规模、任务类型、数据量来调整。我的建议是先用默认值跑起来,然后通过监控发现瓶颈再针对性调整,不要一上来就抄网上的“最优配置”。
6. 常见问题与排查技巧实录
6.1 NameNode启动失败排查
NameNode启动失败最常见的原因是元数据目录权限问题或者端口被占用。排查步骤:
- 看日志:
$HADOOP_HOME/logs/hadoop-*-namenode-*.log,日志里会明确告诉你失败原因。 - 检查目录权限:NameNode进程的用户必须对
dfs.namenode.name.dir有读写权限。 - 检查端口:
netstat -tlnp | grep 9000,看9000端口是否被其他进程占用。 - 检查clusterID:如果之前格式化过多次,NameNode和DataNode的clusterID可能不一致,需要统一。
6.2 DataNode无法连接到NameNode
DataNode启动后如果一直连不上NameNode,通常有几个原因:
- 网络不通:用
telnet namenode-host 9000测试端口连通性。 - 防火墙拦截:检查NameNode和DataNode所在机器的防火墙规则。
- clusterID不一致:DataNode的VERSION文件里的clusterID和NameNode不一致,需要手动改成一致。
- hosts配置错误:
/etc/hosts里主机名和IP的映射不对,导致DataNode解析NameNode地址失败。
6.3 任务运行慢的常见原因
MapReduce任务跑得慢,排查思路可以按这个顺序来:
- 数据倾斜:某个Reduce任务处理的数据量远大于其他Reduce,导致大部分Reduce早就跑完了,就等那一个。解决办法是在Map输出时对key做散列,或者在Reduce端做二次聚合。
- 小文件过多:Map任务数量暴增,每个任务处理的数据量很小,任务启动开销占比过高。解决办法是合并小文件。
- 资源不足:Container申请不到足够的内存或CPU,任务排队等待。检查YARN的队列资源使用情况。
- 磁盘IO瓶颈:Map或Reduce阶段磁盘读写频繁,磁盘利用率接近100%。考虑增加节点或者换SSD。
- 网络瓶颈:Reduce拉取Map输出时网络带宽打满。检查网络流量,考虑调整Map输出压缩。
6.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| NameNode频繁Full GC | 元数据过多,内存不足 | 查看NameNode GC日志 | 加内存、合并小文件、启用Federation |
| DataNode磁盘写满 | 数据增长过快,副本数过多 | df -h查看磁盘使用率 | 加磁盘、降低冷数据副本数、清理临时数据 |
| 任务卡在Map 0% | 输入分片太大或数据本地化失败 | 查看任务日志和YARN资源队列 | 调整分片大小、检查节点健康状态 |
| Reduce拉取数据超时 | 网络抖动或Map输出太大 | 查看Reduce日志中的shuffle阶段 | 增加超时时间、启用Map输出压缩 |
| 集群重启后DataNode未启动 | clusterID不一致或目录权限问题 | 查看DataNode日志 | 统一clusterID、修复目录权限 |
6.5 几个我踩过的坑
第一个坑是NameNode格式化多次。刚搭集群的时候不熟悉,格式化了一次发现配置有问题,改完配置又格式化了一次,结果DataNode起不来。后来才知道格式化会生成新的clusterID,DataNode的clusterID还是旧的,两边对不上。解决办法是把DataNode的数据目录清空,重新启动让它向NameNode注册。
第二个坑是YARN内存配置超过物理内存。有一次把yarn.nodemanager.resource.memory-mb设成了物理内存的100%,结果NodeManager一启动就被系统OOM Killer干掉了。后来改成80%,留出余量给操作系统和DataNode,就稳定了。
第三个坑是Map输出压缩没开。一个任务Map输出有好几百GB,Reduce拉取数据时网络直接打满,任务跑了十几个小时。后来在mapred-site.xml里开了Map输出压缩,网络传输量降到原来的三分之一,任务时间缩短到四个小时。
7. Hadoop的适用边界与演进方向
7.1 什么场景适合用Hadoop
Hadoop最适合的场景是大规模批处理:数据量在TB到PB级别,对延迟不敏感(分钟到小时级),计算逻辑相对简单(比如ETL、日志分析、报表统计)。如果你的数据量只有几十GB,用单机加Pandas或者DuckDB可能更简单高效。如果对延迟要求是秒级甚至毫秒级,那应该用流计算框架而不是Hadoop。
另一个适合的场景是数据归档和冷存储。HDFS的存储成本很低,适合存放大量不常访问但需要保留的数据。配合HDFS的异构存储策略,可以把冷数据自动迁移到更便宜的存储介质上。
7.2 Hadoop生态的演进
Hadoop本身也在演进。HDFS Federation解决了NameNode单点内存瓶颈,ViewFS提供了统一的命名空间视图,Router-based Federation支持跨集群挂载。YARN也在支持更多类型的任务,比如GPU调度、Docker容器调度。
但更大的变化来自外围。Spark取代了MapReduce成为主流计算引擎,因为内存计算快了一个数量级。对象存储(如S3、OSS)在很多场景取代了HDFS,因为存储和计算可以独立扩展。数据湖格式(如Iceberg、Hudi)在HDFS之上提供了ACID事务和增量更新能力。
不过这些演进并没有让Hadoop过时,而是让Hadoop回归了它最本质的角色:一个可靠的分布式存储和资源调度底座。计算引擎可以换,数据格式可以换,但底层的存储和调度逻辑,Hadoop定义的标准依然在被广泛使用。
7.3 给新手的几点建议
如果你刚开始学Hadoop,我的建议是:先跑起来,再深入原理。不要一上来就啃源码,先把伪分布式搭起来,跑一个WordCount,看看任务是怎么提交的、日志是怎么输出的、结果是怎么写的。有了感性认识之后,再去研究NameNode的元数据管理、YARN的调度算法、MapReduce的shuffle过程,会顺畅很多。
另外,不要只学Hadoop。Hadoop是底座,但实际工作中你大概率会用到Spark、Hive、Flink这些上层工具。把Hadoop当成理解分布式系统的入口,而不是终点。理解了HDFS的副本机制,你就能理解为什么Kafka的分区也要做副本;理解了YARN的资源调度,你就能理解K8s的Pod调度逻辑。底层逻辑是相通的。
最后,多动手,多踩坑。看十篇教程不如自己搭一次集群。搭集群的过程中你会遇到各种报错,解决这些报错的过程才是真正长本事的时候。我到现在还记得第一次看到hdfs dfsadmin -report输出所有DataNode正常时的那个感觉,比看任何文档都踏实。