1. 为什么今天还要啃透Hadoop这套“老架构”很多人看到Hadoop第一反应是“这不早过时了吗现在都上云原生、Flink、Spark了。”我去年在某高校实验室带一个分布式日志分析项目时也这么想。结果刚把Spark Streaming跑通数据源突然从Kafka切回了一套老旧的离线日志归档系统——每天20TB原始文本按天分卷存放在本地NAS集群里没有API没有元数据服务只有目录树和一堆.log.gz文件。团队里两个应届生花三天搭好Spark读取逻辑一跑就OOM换Flink做流式预处理数据根本不是流是“冻住的冰川”。最后我们退回HDFSMapReduce重写了核心清洗模块。不是因为怀旧而是因为HDFS的块级定位能力、机架感知副本策略、NameNodeDataNode的强一致性语义在面对这种“海量静态文件弱计算资源高容错要求”的场景时比任何抽象层更高的引擎都更可控、更可预测。这不是技术倒退是回归本质当你的数据规模突破单机IO吞吐瓶颈当你的任务失败成本远高于调度开销Hadoop这套设计哲学——用简单、确定、可推演的机制解决分布式基础问题——反而成了最锋利的刀。它不是被替代了是被封装了。Spark的spark-defaults.conf里还默认配着fs.defaultFShdfs://namenode:9000Flink on Yarn的ApplicationMaster启动前第一步就是向ResourceManager申请Container连Kubernetes上的Alluxio底层缓存策略也大量借鉴HDFS的BlockReport机制。所以这篇不讲“Hadoop有多老”只讲清楚三件事HDFS怎么把一块128MB的文件块稳稳钉在三台不同机架的磁盘上并让你读写时几乎感觉不到网络存在MapReduce怎么把“统计每个URL访问次数”这种业务逻辑拆解成可并行、可重试、可追踪的确定性步骤Yarn怎么从MapReduce的资源管家进化成整个集群的通用调度中枢。这些不是历史文档里的名词是今天排查FileNotFoundException、优化Shuffle spill、诊断Container killed by YARN时你真正要翻的底层说明书。2. HDFS不是“分布式文件系统”而是“分布式块存储系统”很多人误以为HDFS是Linux文件系统的网络版能直接ls /data/logs就看到所有文件。这是最大的认知偏差。HDFS的本质是把文件切割成固定大小的块Block再把块作为独立单元进行存储、复制、定位。文件本身只是逻辑容器块才是物理实体。这个设计决定了它的所有行为逻辑。2.1 块Block一切调度与容错的原子单位HDFS默认块大小是128MBHadoop 3.x远大于传统文件系统如ext4的4KB。为什么这么大不是为了炫技而是为了解决寻道时间Seek Time与传输时间Transfer Time的比值问题。假设磁盘寻道平均10ms顺序读取速度100MB/s那么读取1MB数据寻道10ms 传输10ms 20ms读取128MB寻道10ms 传输1280ms ≈ 1290ms。前者寻道开销占50%后者仅0.8%。HDFS的设计目标是让一次磁盘IO尽可能多地传输数据把昂贵的寻道成本摊薄到极致。提示块大小不是越大越好。如果块设为1GB单个MapTask处理时间可能长达数小时一旦失败重跑成本极高。128MB是经验平衡点既大幅降低寻道占比又保证单Task执行时间在分钟级便于Yarn调度和故障恢复。块在HDFS中是不可变Immutable的。你无法对一个已写入的块追加内容或修改中间字节。所有写操作都是创建新块。这看似限制实则是可靠性的基石——避免了并发修改导致的数据不一致简化了副本同步逻辑。当你执行hdfs dfs -put local.txt /hdfs/path.txt客户端做的第一件事不是传文件而是向NameNode申请一批块ID如blk_1073741825,blk_1073741826每个块对应一个128MB的存储单元。NameNode根据机架拓扑、DataNode负载、现有副本数为每个块分配3个DataNode默认副本数返回给客户端一个“写入管道Pipeline”。2.2 写入流程一次写入三次落盘全程可追溯HDFS写入不是“发给NameNode它分发给DataNode”而是一个客户端驱动的流水线写入Pipeline Write。以写入一个256MB文件为例含2个块完整流程如下客户端发起请求调用FileSystem.create()指定路径/data/app.log。客户端向NameNode发送RPC请求“我要创建这个文件并获取第一个块的写入位置”。NameNode分配块与节点NameNode检查路径权限、父目录是否存在、是否已有同名文件。确认后生成第一个块IDblk_1073741825并根据机架感知Rack Awareness算法选择3个DataNode。典型策略是1个副本在本机架的DataNode A1个在同机架的DataNode B第3个在不同机架的DataNode C。这样既保证同机架内低延迟传输又确保单机架故障时数据不丢失。建立写入管道NameNode将块ID和3个DataNode地址A→B→C返回给客户端。客户端与DataNode A建立TCP连接A再与B建立连接B再与C建立连接形成一条A→B→C的管道。数据流式写入与校验客户端将数据按包Packet切分默认64KB每个Packet附带校验和Checksum。它把Packet发给AA收到后立即写入本地磁盘但不刷盘先存Buffer同时将Packet转发给BB收到后同样操作再转发给CC收到后写入本地磁盘并向B发送ACKB收到C的ACK向A发送ACKA收到B的ACK向客户端发送ACKACK确认与块提交客户端收到A的ACK表示该Packet已在3个节点落盘内存Buffer。当一个块128MB的所有Packet都得到ACK客户端向NameNode发送“块完成”报告。NameNode将该块的元数据块ID、所属文件、3个DataNode地址写入内存并持久化到EditLog事务日志。此时该块才被视为“已提交”对读操作可见。重复流程写入下一块客户端继续为第二个块blk_1073741826向NameNode申请位置重复步骤2-5。注意整个过程NameNode不参与数据传输只负责元数据管理和节点调度。数据流完全由客户端驱动在DataNode间管道式传递。这极大减轻了NameNode压力也避免了中心化数据瓶颈。这也是为什么HDFS能支撑PB级数据——瓶颈在磁盘IO和网络带宽不在NameNode CPU。2.3 读取流程就近原则下的多副本协同读取比写入更轻量但也充满智慧。当你执行hdfs dfs -cat /data/app.log流程如下客户端解析路径客户端向NameNode发送RPC“请告诉我/data/app.log这个文件由哪些块组成每个块在哪些DataNode上”NameNode返回块位置列表NameNode查询内存中的元数据返回一个有序的块列表。对每个块返回其3个副本所在的DataNode地址并按网络距离Network Distance排序。排序规则是本机Client Host 同机架Same Rack 不同机架Different Rack。例如块blk_1073741825的副本在dn1-rack1,dn2-rack1,dn3-rack2而客户端恰好运行在dn1-rack1上则返回顺序为[dn1-rack1, dn2-rack1, dn3-rack2]。客户端直连最优DataNode客户端拿到第一个块的位置列表后不经过NameNode直接与列表中第一个DataNodedn1-rack1建立连接发起读取请求。DataNode从本地磁盘读取该块数据通过TCP流式返回给客户端。自动故障转移如果客户端连接dn1-rack1失败超时或拒绝它会立即尝试列表中的第二个节点dn2-rack1无需再次询问NameNode。NameNode只在第一次读取时提供位置后续重试完全由客户端自主决策。并行读取多块对于大文件客户端会为每个块单独建立连接并行读取。HDFS Client内部有连接池管理避免频繁建连开销。这个设计的关键在于去中心化读取。NameNode只做一次“指路”之后所有数据流动都在客户端和DataNode之间发生。这使得读取性能几乎线性扩展——增加DataNode就能提升总吞吐NameNode不会成为瓶颈。而“就近原则”则最小化了跨机架网络流量降低了延迟和带宽消耗。2.4 NameNode集群的“大脑”与“心脏”也是单点风险所在NameNode是HDFS的绝对核心但它并非传统意义上的“服务器”。它是一个纯内存元数据服务其状态由两部分构成内存元数据In-Memory Namespace包含所有文件/目录的树状结构INode、每个文件对应的块列表BlocksMap、每个块的副本位置LocatedBlocks。这是所有读写操作的实时依据必须常驻内存。持久化日志EditLog FsImageEditLog记录所有元数据变更如create file,delete block,add replica是WALWrite-Ahead LogFsImage是某一时刻内存元数据的完整快照。NameNode启动时先加载FsImage到内存再重放EditLog中所有后续操作恢复到最新状态。踩坑经验NameNode的JVM堆内存必须足够大。一个拥有1亿个文件的集群INode对象可能占用20GB以上内存。如果堆内存不足频繁GC会导致NameNode响应迟钝客户端出现Connection refused或Standby Exception。我们曾在一个测试集群因堆内存设为4G导致NameNode每5分钟Full GC一次整个集群读写延迟飙升至秒级。最终调至32G并启用G1GC问题消失。NameNode的单点问题通过HAHigh Availability模式解决。HA模式下有两个NameNodeActive主和Standby备。它们共享一个Quorum Journal ManagerQJM集群通常3或5个JournalNode。Active NameNode将所有EditLog写入QJMStandby NameNode持续从QJM读取并应用这些日志保持内存状态与Active完全同步。当Active故障ZooKeeper触发故障转移FailoverStandby升级为Active整个过程秒级完成客户端无感知。这彻底消除了SPOFSingle Point of Failure。3. MapReduce不是编程模型而是分布式计算的“确定性契约”MapReduce常被误解为一种“写两个函数map/reduce就能跑分布式”的简易框架。实际上它是Hadoop为解决“如何在不可靠硬件上执行任意用户代码并保证结果正确”这一难题所定义的一套严格、可验证、可审计的执行契约。它的价值不在于语法简洁而在于用一套固定的、可穷举的阶段将混沌的分布式执行约束在确定性的轨道上。3.1 架构四层角色各司其职边界清晰MapReduce v1经典MR的架构非常清晰分为四个核心角色Client客户端用户提交作业Job的入口。它负责将用户编写的Mapper/Reducer类、配置文件job.xml、输入输出路径等打包成一个Jar。向JobTracker申请一个Job ID。将Jar、配置、切片信息Split上传到HDFS的/user/history目录。向JobTracker提交作业。JobTracker作业跟踪器集群的“总调度官”。它负责接收Client提交的作业将其放入作业队列。资源管理监控所有TaskTracker的心跳掌握集群可用SlotCPU核内存。任务调度根据数据本地性Data Locality原则将Map Task分配给存储了该InputSplit数据的DataNode上的TaskTracker即“计算向数据移动”将Reduce Task分配给资源充足的TaskTracker。容错管理监控TaskTracker心跳若超时未收到标记其上所有Task为失败监控Task状态若某个Task失败超过4次默认则整个Job失败。TaskTracker任务跟踪器运行在每个DataNode上的代理进程。它负责定期向JobTracker发送心跳包含本节点资源使用率、正在运行的Task列表。接收JobTracker下发的Map/Reduce Task指令。在本地JVM中启动一个子进程Child JVM来执行具体的Mapper或Reducer逻辑。将Task的stdout/stderr、计数器Counter值、状态更新上报给JobTracker。Task任务实际执行用户代码的单元。分为两类Map Task处理一个InputSplit通常是HDFS的一个块或多个小文件合并成的逻辑切片输出key, value对到本地磁盘的环形缓冲区Spill Buffer。Reduce Task从所有Map Task的输出中拉取Shuffle属于自己分区Partition的数据进行合并Sort Merge然后调用用户Reducer处理。关键理解MapReduce的“分布式”不是靠用户代码实现的而是由JobTracker和TaskTracker这套基础设施强制注入的。用户只需关注map()和reduce()函数的逻辑框架保证了1输入被均匀切分2Map输出按Key哈希分区3Reduce能拉取到所有属于自己的数据4失败任务自动重试。这是一种“契约式编程”——你遵守接口框架保证分布式语义。3.2 执行原理从InputSplit到OutputFile的七步炼金术一个MapReduce Job的生命周期是七个严格定义的阶段。理解每一步的输入、输出、触发条件和失败影响是调试性能和错误的核心。步骤1InputSplit切分客户端完成Client在提交Job前调用InputFormat.getSplits()方法。对HDFS文件默认是FileInputFormat它会计算文件总大小如1GB。根据mapreduce.input.fileinputformat.split.minsize默认1B和maxsize默认Long.MAX_VALUE结合HDFS块大小128MB决定切分数。对于1GB文件理想切分是8个128MB的Split1024/1288。每个Split是一个逻辑描述包含文件路径、起始偏移量start、长度length、所在主机列表用于数据本地性。注意Split不是物理块一个Split可以跨越多个HDFS块如小文件合并也可以一个HDFS块被切成多个Split如果minSize设得很大。Split的目的是让Map Task有“合适大小”的工作单元而非绑定HDFS块。步骤2Map Task执行TaskTracker上每个Map Task启动一个Child JVM执行以下流程ReadRecordReader如LineRecordReader从InputSplit指定的文件位置开始逐行读取解析成key, value如offset, line_content。Map调用用户Mapper.map(key, value, context)。Context将输出的k, v写入环形缓冲区Spill Buffer默认100MB。Spill Sort当缓冲区使用率达80%默认spill.percent启动溢出Spill线程对缓冲区内所有k, v按Key进行快速排序QuickSort。按Partitioner默认HashPartitioner计算每个k, v的分区号0~R-1R为Reduce Task数。将排序后的数据按分区写入本地磁盘的spillN.out文件并生成索引文件spillN.out.index记录每个分区的起始偏移。Merge当Map Task结束所有spill*.out文件需要合并成一个file.out。合并过程是归并排序MergeSort同时进行Combiner如果配置了的局部聚合减少网络传输量。步骤3ShuffleReduce端主动拉取这是MapReduce最耗时、最易出错的阶段完全由Reduce Task驱动CopyReduce Task启动Fetcher线程向所有Map Task所在节点的HttpServerMapTask启动的轻量HTTP服务发起HTTP GET请求拉取属于自己分区的数据如partition_0。为防网络拥塞Reduce Task默认同时拉取5个Map的输出mapreduce.reduce.shuffle.parallelcopies。Merge拉取到的数据先存入内存缓冲区inMemoryMerger当内存满或拉取完成启动OnDiskMerger将内存数据和磁盘上的spill*.out文件进行归并排序生成最终的reduce_input文件。此文件按键Key全局有序。踩坑经验Shuffle阶段OutOfMemoryError极其常见。根本原因不是Reduce内存不够而是mapreduce.reduce.merge.inmem.threshold内存合并阈值和mapreduce.reduce.shuffle.input.buffer.percent用于拉取的堆内存比例设置不合理。我们曾将inmem.threshold设为1000即1000个键就触发内存合并导致大量小文件在内存中堆积最终OOM。改为0禁用内存合并全部走磁盘后虽然慢一点但稳定了。步骤4Reduce执行TaskTracker上Reduce Task从reduce_input文件中按Key分组GroupingComparator对每个Key调用一次Reducer.reduce(key, values, context)。values是一个迭代器遍历所有属于该Key的Value。Reducer的输出由OutputFormat如TextOutputFormat写入HDFS的/output/part-r-00000等文件。步骤5-7Commit、Cleanup与HistoryCommit所有Reduce Task成功后JobTracker通知ClientClient调用OutputCommitter.commitJob()将临时输出目录如/output/_temporary/0/重命名为最终输出目录/output/。这是原子操作保证作业要么全成功要么全失败。Cleanup无论成功失败OutputCommitter.cleanupJob()都会被调用清理临时文件。HistoryJob的历史信息配置、计数器、任务日志链接被写入HDFS的/history/目录供mapred job -history命令查询。这套七步流程每一个环节都有明确的输入输出契约、失败重试策略和日志记录点。当你看到Task attempt failed to report status就知道是TaskTracker与JobTracker心跳断了看到Shuffle failed with too many fetch failures就知道是网络或Map端服务挂了。这不是黑盒而是一张可追踪、可审计的执行地图。4. YARN从MapReduce专属管家到集群通用操作系统YARNYet Another Resource Negotiator的诞生是Hadoop生态的一次范式革命。它回答了一个根本问题为什么一个集群只能运行MapReduce一种计算框架YARN将“资源管理”和“作业调度”彻底解耦把Hadoop集群变成了一个通用的分布式操作系统MapReduce、Spark、Flink、Presto等都只是运行在其上的“应用程序”。4.1 工作机制双层调度责任分离YARN的核心思想是“两级调度”第一级是集群资源的宏观分配第二级是单个应用的微观任务调度。这打破了MRv1中JobTracker既是资源管家又是应用调度官的“大包大揽”模式。ResourceManagerRM集群的“总资源经理”。它只有一个HA模式下有Active/Standby职责纯粹全局资源视图维护所有NodeManagerNM的心跳掌握集群总CPU、内存、磁盘、网络资源。资源分配接收来自ApplicationMasterAM的资源请求如“我要2个Container每个2核4GB内存”根据调度策略如CapacityScheduler或FairScheduler进行分配并通过AllocateResponse返回给AM。不关心应用逻辑RM不知道你跑的是MapReduce还是Spark它只认Container这个资源单元。NodeManagerNM运行在每个节点上的“资源代理”。它取代了MRv1的TaskTracker职责聚焦本地资源监控定期向RM汇报本节点的资源使用情况CPU、内存、磁盘、网络。Container生命周期管理接收RM下发的StartContainer指令启动一个Container本质是Linux cgroup隔离的进程组监控其资源使用若超限则Kill。日志管理收集Container的标准输出和错误日志上传到HDFS。ApplicationMasterAM每个应用的“专属管家”。它是一个由客户端提交的应用程序运行在某个Container中。它的核心职责是应用级调度与RM协商为应用的各个任务如Map Task、Spark Executor申请、释放Container。任务协调与NM通信启动、监控、重启具体任务进程。容错若自身失败RM会重新启动一个新的AM实例并恢复其状态需应用支持CheckPoint。关键对比在MRv1中JobTracker既要管“集群有多少资源”又要管“这个Job的第3个Map Task该在哪跑”。在YARN中RM只管“集群还有多少空闲资源”AM自己去抢、去调度。这就像酒店前台RM只告诉你“还有10个房间空着”而每个旅行团AM自己决定带多少人、住哪几间、谁睡上铺。4.2 ContainerYARN的“进程沙箱”统一资源计量单位Container是YARN最核心的抽象它是一个资源受限的进程执行环境。你可以把它理解为一个轻量级的、由cgroup控制的Linux进程组。每个Container被分配CPU资源通过yarn.nodemanager.resource.cpu-vcores配置以虚拟核vcore为单位。YARN本身不精确控制CPU时间片而是依赖Linux cgroup的cpu.shares进行权重分配。内存资源通过yarn.nodemanager.resource.memory-mb配置以MB为单位。这是硬性限制超出会被cgroup OOM Killer杀死。其他资源可扩展如GPU、FPGA通过ResourceTypes配置。当AM向RM申请资源时它请求的是“N个Container每个X vcore Y MB内存”。RM批准后将Container的规格IP、端口、环境变量、启动命令发给目标NM。NM收到后执行docker run或cgroup exec启动一个隔离进程。这个进程就是你的Map Task、Spark Executor或Flink TaskManager。实操心得Container内存设置是性能关键。mapreduce.map.memory.mbMap Task内存和mapreduce.reduce.memory.mbReduce Task内存必须小于NM的yarn.nodemanager.resource.memory-mb否则RM无法分配。我们曾将map.memory.mb设为8192MB而NM只配置了6144MB导致所有Map Task永远处于ACCEPTED状态卡在资源等待队列。查看RM Web UI的Scheduler页面一眼就能看到“Pending Resources”居高不下根源在此。4.3 Application生命周期从提交到退出的完整闭环一个YARN应用的启动是一个标准的、可预测的流程Client提交用户执行yarn jar xxx.jar MyApp。Client将应用Jar、启动类、配置打包向RM发送SubmitApplicationRequest。RM启动AMRM为该应用分配第一个Container称为AppMaster Container并将启动命令如java -Xmx1g MyAppMaster发给目标NM。NM启动AM进程。AM注册与资源申请AM启动后向RM注册自己并开始循环调用allocate()方法申请更多Container来运行实际任务。例如一个MapReduce AM会申请N个Map Container数量InputSplit数M个Reduce Container数量mapreduce.job.reduces配置值NM启动任务RM批准Container后将规格发给NM。NM启动任务进程如java -Xmx2g Mapper任务进程开始执行用户逻辑。AM监控与容错AM通过NMClient与NM通信监控任务状态。若任务失败AM可申请新的Container重跑若NM宕机AM会收到NodeRemovedEvent将该NM上所有任务标记为失败并重试。应用完成当所有任务成功AM向RM发送FinishApplicationMasterRequest声明应用结束。RM清理该应用的所有资源记录。AM进程自行退出。日志归档NM将所有Container的日志按应用ID归档到HDFS的/tmp/logs/目录供yarn logs -applicationId id命令查询。这个流程的精妙之处在于完全解耦。RM的代码库不需要知道MapReduce的任何细节它只处理ContainerAM的代码库也不需要知道YARN的调度算法它只和RM的RPC接口打交道。这正是YARN能支撑Spark、Flink等数十种框架的根本原因——它们各自实现一个符合YARN AM规范的“管家”就能无缝接入。5. 三者关系HDFS、MapReduce、YARN如何拧成一股绳把HDFS、MapReduce、YARN看作三个独立模块是危险的。它们是一个精密咬合的齿轮组共同构成了Hadoop分布式数据处理的基石。理解它们如何协同才能真正驾驭这个系统。5.1 数据流视角从存储到计算的闭环想象一个典型的WordCount作业起点HDFS。输入文件/input/words.txt1GB被HDFS按128MB切分成8个块分布在集群10个DataNode上每个块有3个副本。触发Client提交MR Job。Client读取HDFS的/input/words.txt元数据计算出8个InputSplit并将Job提交给YARN。资源调度YARN介入。YARN RM为该Job分配一个AM Container。AM启动后向RM申请8个Map Container。RM根据数据本地性Data Locality策略优先将Map Container分配给存储了该InputSplit主副本的DataNode。例如Split1的主副本在dn1-rack1那么RM会尽量把Map1 Container分配给dn1-rack1上的NM。计算执行Map Task读取本地数据。dn1-rack1上的NM启动Map1 Container。该Container内的Mapper进程通过HDFS Client API直接从本地dn1-rack1的DataNode读取Split1的数据零网络IO。处理完后输出到本地磁盘的spill.out。Shuffle跨节点数据搬运。Reduce Task运行在dn5-rack2需要从dn1-rack1,dn2-rack1,dn3-rack2等所有Map节点拉取属于自己分区的数据。这时HDFS的DataNode HTTP服务http://dn1:50075被用作数据传输通道YARN的Container网络隔离保证了传输安全。结果落盘HDFS再登场。Reduce Task完成计算将最终结果word, count写入HDFS的/output/目录。HDFS Client再次介入将结果块按规则写入DataNode。整个链条中HDFS提供可靠、本地化的数据存储YARN提供灵活、隔离的资源调度MapReduce作为YARN上的一个Application提供确定、可审计的计算逻辑。三者各守其位又环环相扣。5.2 容错视角层层防御的可靠性体系Hadoop的高可用不是靠单一组件的强壮而是靠三层冗余设计HDFS层容错基于块的多副本3份和机架感知。单个DataNode宕机数据仍可从其他副本读取单个机架断电仍有跨机架副本可用。NameNode HA通过QJM和ZooKeeper实现秒级故障转移。YARN层容错ResourceManager HAActive/Standby保证资源调度不中断NodeManager的健康检查心跳超时自动剔除故障节点ApplicationMaster的失败重启由RM触发保证应用不因AM崩溃而终止。MapReduce层容错这是最细粒度的容错。Map Task失败AM会重新申请Container在另一个节点重跑Reduce Task失败AM会重新拉取数据重算甚至单个Map输出文件损坏Shuffle阶段的校验和Checksum会检测到并触发重拉。MapReduce的“重试上限”mapreduce.map.maxattempts和“失败容忍度”mapreduce.reduce.failures.allowed是可配置的允许你在“严格正确性”和“快速失败”间权衡。这三层容错像三道防火墙。HDFS保证数据不死YARN保证计算不瘫MapReduce保证逻辑不错。任何一层的故障都不会导致整个作业的雪崩式失败。5.3 性能调优视角参数背后的物理世界所有Hadoop调优最终都指向对物理资源的精准控制。以下是几个关键参数及其物理意义参数默认值物理含义调优建议dfs.blocksize128MB单次磁盘IO的数据量大文件TB级可增至256MB小文件GB级保持128MB极小文件MB级考虑用SequenceFile合并mapreduce.map.memory.mb1024MBMap Task JVM堆内存上限必须 ≤ NM的yarn.nodemanager.resource.memory-mb若Map逻辑复杂如JSON解析可增至2048MB但需相应减少并发Map数mapreduce.task.io.sort.mb100MBMap环形缓冲区大小影响Spill频率增大可减少Spill次数但占用更多内存建议设为map.memory.mb的30%-50%yarn.scheduler.minimum-allocation-mb1024MBRM分配Container的最小内存单位若NM有32GB内存可设为2048MB避免碎片化但设得过大小任务无法获得资源mapreduce.reduce.shuffle.parallelcopies5Reduce并发拉取Map输出的线程数网络带宽充足时可增至10-15加速Shuffle但过多线程会增加NM CPU压力最后一个实战技巧永远用yarn top和hdfs dfsadmin -report作为调优起点。yarn top显示实时的Container分布和资源使用一眼看出哪个节点过载hdfs dfsadmin -report显示DataNode的磁盘使用率、块数、健康状态帮你判断是否需要均衡hdfs balancer或下线故障节点。工具永远比猜测更可靠。