尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

HDFS、YARN、MapReduce 原理拆解与实战指南

HDFS、YARN、MapReduce 原理拆解与实战指南 搞懂 Hadoop 生态绕不开 HDFS、YARN、MapReduce 这三句话。很多刚接触分布式系统的人被 NameNode、DataNode、ResourceManager、Container、Shuffle 这些名词砸得晕头转向面试时被问一句“MapReduce 的 Shuffle 到底经历了什么”就卡壳。这篇不是教科书复读而是把三者的原理和工作流程拆开揉碎从架构设计到读写链路再到作业提交的完整生命周期中间穿插可直接复制的命令、配置和排障经验。无论你是准备大数据面试、刚开始搭集群还是已经在写 MapReduce 作业但总感觉哪里没吃透这篇文章都适合当一份能随时翻出来的实操笔记。1. 先搞清楚HDFS、YARN、MapReduce 到底各管什么1.1 一个比喻仓库、调度台和流水线把 Hadoop 集群想象成一家大型快递公司。你需要一个巨大的仓库来放包裹这是 HDFS需要一个调度台来分配车辆、人员和装卸工这是 YARN还需要一套快递分拣、运输、签收的流水线规则这是 MapReduce。HDFS 做的事很简单就是把大文件切成固定大小的数据块block分散存到多台机器的磁盘上。默认一个 block 是 128MB副本数默认 3所以一份数据实际上有主副本和额外备份放在不同节点上。这样单块磁盘坏了数据不会丢单台机器瓶颈了可以从别的机器读取。YARN 做的事更抽象它不管数据内容只关心集群里有多少可用的 CPU 和内存然后把这些资源切成“容器”Container按需分配给作业。它就像调度台不关心你运的是衣服还是电子产品只问你几辆车、几个人、几点出发。MapReduce 则定义了一套分布式计算模型先把大任务拆成小任务并到集群各节点上并行执行Map再把结果规约成一个聚合答案Reduce。核心思想是“计算向数据移动”把处理逻辑下发到数据所在节点而不是把海量数据搬到一台机器上。1.2 为什么拆成三块而不是一块早期 Hadoop 版本里存储是 HDFS计算和资源管理却全部打包在一个叫 JobTracker 的进程里。JobTracker 既要负责调度作业又要监控每个任务的运行状态还要维护历史数据。集群一上百台节点JobTracker 就变成瓶颈而且单点故障直接影响整个集群。后来把“资源调度”和“作业监控”两条职责拆开就诞生了 YARN。ResourceManager 只做资源分配每个作业分配一个独立的 ApplicationMaster 去管理自己的任务彻底解除单点压力。拆开之后还有个好处Spark、Flink、Tez 等计算框架只需要对接 YARN 的资源接口就能跑在同一个集群上。存储、资源、计算三个层面各自独立演进又通过一套约定协同工作这是 Hadoop 能长期占据离线处理主流的底层原因。2. HDFS 原理存储层的读写闭环2.1 架构NameNode、DataNode 与副本机制HDFS 是典型的 master/slave 架构。主节点叫 NameNode负责维护整个文件系统的元数据相当于仓库的账本记录每个文件的路径、权限、block 列表、block 副本放在哪些机器上。真正存数据的是 DataNode每台 DataNode 管理本机磁盘上的数据块并周期性地向 NameNode 上报自己的状态和 block 列表。还有一个容易被误解的角色叫 SecondaryNameNode。它不是主节点的实时热备也不承担自动故障转移。它的主要工作是定期合并 NameNode 的编辑日志EditLog和镜像文件FsImage帮助 NameNode 减少重启时恢复元数据的时间。很多人以为开着 SecondaryNameNode 就高可用了实际高可用要靠两台 NameNode 加 JournalNode 组成 Active/Standby 模式。副本放置策略是 HDFS 可靠性的关键。默认 3 副本时第一副本放在客户端所在节点如果是集群外部提交则随机选一个负载低的节点第二副本放在与第一副本不同机架的节点第三副本放在与第二副本同机架但不同机器的节点。这样既能容忍单机故障又能容忍整个机架断电同时保证跨机架读数据时网络开销不至于过大。2.2 写入一条链路客户端、流水线与 ack我一开始以为 HDFS 写入就是把文件“传”给 NameNode实际完全不是。NameNode 只负责“开单”不碰数据流。完整流程如下客户端先调用 DistributedFileSystem 的 create 方法向 NameNode 发起“我要创建 /data/xxx 文件”的请求。NameNode 检查路径是否存在、父目录是否存在、客户端权限是否足够全部通过后创建文件元数据记录返回一个输出流。接下来客户端把文件逻辑上切成 block默认 128MB 一个。拿到一个 block 后客户端向 NameNode 询问“该往哪些 DataNode 写副本”NameNode 按机架感知策略返回一份 DataNode 列表比如 node1、node2、node3。真正写入时走的是流水线模式客户端把数据按 64KB 的 packet 切包每个 packet 又由多个 chunk 组成。第一个 packet 先发给 node1node1 一边落盘一边把同一个 packet 转发给 node2node2 转给 node3。每个 DataNode 写完一个 packet 后会向前一个节点返回 ack最终回到客户端。客户端收到 ack 才继续发下一个 packet。这个设计很像水管注水发一个确认一个保证数据不会丢在半路。如果写入过程中某个 DataNode 故障客户端会从管线里剔除这个节点用剩余节点继续完成副本写入等后续副本数不足时再自动补副本。所有 block 写完客户端调用 close 关闭输出流NameNode 这时才把文件标记为“已完成”文件对用户可见。所以 HDFS 里“写完”是个最终一次性提交而不是边写边可见。2.3 读取一条链路元数据定位与就近读取读取比写入简单但也讲策略。客户端先调用 open 方法拿到输入流实际是向 NameNode 拿文件的元数据这个文件有哪些 block每个 block 在哪些 DataNode 上。NameNode 返回 block 位置列表时会按网络拓扑给 DataNode 排序距离最近的排在最前面。客户端随即跳开 NameNode直接和 DataNode 建立 TCP/套接字连接读取数据。读取过程中客户端会做端到端的校验用每个 chunk 自带的校验和验证数据有没有损坏。一旦发现一个副本有损坏会立即切换到另一个副本读同时报告 NameNode 记录损坏 block 并调度新副本。这就是“就近读取 自动容错”的基本面貌。关于“短电路读”值得一提。大多数客户端进程不在 DataNode 上需要通过 DataNode 的 socket 转发数据如果客户端和 DataNode 在同一台机器HDFS 可以开启短电路读让客户端直接打开本地文件绕过网络栈和 DataNode 转发减少一次数据拷贝延迟和吞吐都能改善。生产集群大量使用计算存储混布时这个优化非常有效。2.4 常用命令、fsck 与现实排障命令行是排障的第一工具。下面这组命令我几乎每天都用hdfs dfs -mkdir -p /user/app/logs hdfs dfs -put local.log /user/app/logs/ hdfs dfs -cat /user/app/logs/local.log | head -n 50 hdfs dfs -du -h /user/app/logs hdfs dfsadmin -report hdfs fsck /user/app/logs -files -blocks -locationsfsck 这个名字延续了 Unix 文件系统检查工具的叫法。跑完会输出每个文件的 block 状态比如“Total blocks”和“Missing replicas”。这里有个经验fsck 只负责发现和报告问题它不会修复坏块。发现缺失副本后要人工介入或者等 DataNode 重新上线后由 NameNode 自动补副本。如果 fsck 报“Access denied”或权限不足先确认你是不是操作了别的用户的目录HDFS 的权限模型默认类似 POSIX但默认配置甚至不开启强鉴权生产环境需要配合 Kerberos 或 Ranger 等方案。还有一个高频场景误删文件。HDFS 默认有一个回收站机制只要配置了 fs.trash.interval用普通 delete 删除的文件会先进到 /user/xxx/.Trash 目录在保留时间内能捞回来。这个配置在生产务必打开我见过太多手滑 rm 的事故。3. YARN 原理从资源调度到作业调度3.1 MRv1 到 YARN调度与监控的拆分旧版 MapReduce 的资源管理把 JobTracker 当全能管家但它做的事太多既要管“哪个作业先跑”又要管“每个 map/reduce 任务跑在哪、失败没”集群稍大就成单点瓶颈。YARN 的诞生思路很质朴把“分配资源”和“管理作业”分开。ResourceManager 只管资源分配和整个集群的全局状态不关心具体某个 map 任务的死活。具体作业的“项目管理工作”由 ApplicationMaster 承担每个作业单独起一个。这样 ResourceManager 负载大幅下降而且坏了某台节点上的任务不会拖垮整个集群调度。另一个重要变化是资源模型。MRv1 只按“槽位”slot分配map slot 和 reduce slot 不能互相借用资源利用不充分。YARN 用 Container 抽象资源每个 Container 指定 CPU 核数和内存大小map 任务和 reduce 任务都跑在 Container 里按需申请用完即收细致很多。3.2 四大角色的职责边界YARN 的核心角色可以概括为“一主一从、一作业一容器”。ResourceManagerRM是全局主节点它维护整个集群的资源总账接收新作业的提交请求启动并监管每个作业的 ApplicationMaster。它不做具体任务监控这是故意的目的是把压力降下来。NodeManagerNM是每台机器上的从节点负责启动和管理本机上的 Container周期性向 RM 汇报本节点的资源剩余情况并处理来自 AM 的容器启动请求。ApplicationMasterAM是每个作业的专属管理进程。作业提交之后RM 会在某个空闲节点上启动它。AM 负责向 RM 申请资源获得资源后通知对应节点的 NodeManager 启动容器接着跟踪任务进度处理任务失败重试。作业结束时 AM 注销并释放自己的容器。Container 表面上是资源描述包含内存、CPU 核数等实际执行时是 NodeManager 启动的一个 Java 进程或者一组进程。说白了一个 Container 约等于一台“微型虚拟机”只不过隔离性弱于真正虚拟机。3.3 作业提交到 YARN 的完整流程以hadoop jar xxx.jar 主类 输入输出为例流程是这样的客户端提交作业给 RMRM 返回一个 application ID。RM 在某个 NodeManager 上启动一个 Container里面运行 ApplicationMaster。AM 启动后先向 RM 注册自己然后向 RM 提交资源申请申请内容通常是“我需要 X 个 map 容器、Y 个 reduce 容器每个容器多少内存多少 CPU”。RM 收到申请后结合当前节点的资源余量和队列配额把符合条件的 Container 分配给 AM。AM 拿着 Container 列表去和对应 NodeManager 通信让 NM 启动容器并拉起任务进程。每个任务进程启动后会反向向 AM 汇报状态AM 把进度汇总展示。如果某个任务失败AM 会重新申请容器重试。全部任务完成后AM 向 RM 注销并释放资源RM 更新全局资源表作业结束。这套流程下RM 完全不需要知道任务具体干了什么分工十分干净。这里有个考点YARN 的“作业”概念不是 Hadoop MR 独有的。Spark on YARN、Flink on YARN 都遵守“RM 起 AMAM 分配资源”的模式只是 AM 的角色和心跳协议细节不同。3.4 三种调度器的选型思路YARN 提供了三种调度器个个用途不同。FIFO 调度器最简单作业按提交顺序一个一个来像排队打饭。优点是可控、无抢占缺点是队首作业只要是大任务后面作业全部饿死。单用户内部测试时用没问题多团队共享集群就很不合适。Capacity Scheduler 是 Hadoop 默认调度器。它按队列划分资源比例比如 A 团队分 60%B 团队分 40%。每个队列内部又可以用 FIFO 或公平策略。好处是多团队互不干扰即使 B 队列的作业很重也只能使用配额内的资源不会把 A 队列挤垮。Fair Scheduler 追求“所有运行作业平均分资源”。新作业一提交运行中的作业会让出资源腾位置默认最多等一段时间才强制抢占。它适合交互式查询和批处理混合的场景但频繁抢占可能导致任务反复失败。选型不要盲目。日常离线跑批、集群按部门共享优先用 Capacity临时试验集群或小团队用 FIFO 足够如果集群要跑大量短任务并兼顾长任务再考虑 Fair。4. MapReduce 原理分治、Shuffle 与排序4.1 Map 与 Reduce 的编程模型MapReduce 把分布式计算抽象成两个阶段。Map 阶段输入是键值对输出也是键值对但它不负责汇总只负责“切碎”和“初步归类”。Reduce 阶段接收同一个 key 下的所有 value 列表做最终聚合。这很像流水线作业仓库收到一批快递输入分片分拣员按收件城市分拣map把同一城市的快递堆在一起partition 和 shuffle再由装车员统计并打包reduce。输入切分由 InputFormat 负责。一个输入分片split通常对应一个 block但不是绝对的一对一。split 过大时 map 数量少并行度低过小时 map 数量暴增、元数据开销大所以 FileInputFormat 的切片大小默认等于 block 大小也就是 128MB。Map 阶段输出的 KV 不会立即写 HDFS而是先写到本地磁盘因为中间结果只有计算过程中的临时状态不值得三副本存储和网络传输。这是 MapReduce 性能调优的重要概念很多人想不通为什么 shuffle 数据算 IO 开销原因就在这里。4.2 Shuffle 全流程环形缓冲区、分区、排序、归并Shuffle 是 MapReduce 的灵魂也是面试最常追问的部分。Map 端输出的每个 KV 会先进入一个内存环形缓冲区默认大小是mapreduce.task.io.sort.mb通常是 100MB。写入比例达到mapreduce.map.sort.spill.percent默认 0.8后一个后台线程会开始把缓冲区数据溢写到本地磁盘。溢写前不是直接写而是先做两件事分区和排序。数据按 key 的哈希值分区保证相同 key 进同一个 reduce分区内部按 key 排序。溢写过程中如果配置了 Combiner也叫本地规约器会对同一分区的数据先做一次小聚合减少最终传输量。一个 map 任务可能溢写多个文件map 结束时会做一个归并merge把多个溢写文件合并成一个输出文件同时再次做分区和排序。Reduce 端要拉数据时通过网络按分区位置拉取属于自己那份的中间结果。Reduce 端收到数据后先放内存内存不够就溢写本地最后把所有文件归并排序成一个大文件。归并完成后才进入 reduce 函数调用一次 reduce 方法处理一个 key 及其 value 迭代器。刚才提到的排序默认是全排序。map 端按 key 排序reduce 端对多个 map 输出做归并排序所以最终每个 reduce 输出的内部数据都是有序的。基于这一点你才能实现“分组排序”“倒排索引”“Top N”这类复杂逻辑。4.3 一个能直接复制的 WordCount 实例绕开所有花哨案例先从词频统计这个“Hello World”切入。先写 Mapper 类public class WordCountMapper extends MapperLongWritable, Text, Text, IntWritable { private final static IntWritable one new IntWritable(1); private Text word new Text(); Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String line value.toString(); StringTokenizer itr new StringTokenizer(line); while (itr.hasMoreTokens()) { word.set(itr.nextToken()); context.write(word, one); } } }再写 Reducerpublic class WordCountReducer extends ReducerText, IntWritable, Text, IntWritable { private IntWritable result new IntWritable(); Override protected void reduce(Text key, IterableIntWritable values, Context context) throws IOException, InterruptedException { int sum 0; for (IntWritable val : values) { sum val.get(); } result.set(sum); context.write(key, result); } }Driver 里设置作业参数并提交Job job Job.getInstance(new Configuration(), word count); job.setJarByClass(WordCountDriver.class); job.setMapperClass(WordCountMapper.class); job.setReducerClass(WordCountReducer.class); job.setOutputKeyClass(Text.class); job.setOutputValueClass(IntWritable.class); FileInputFormat.addInputPath(job, new Path(args[0])); FileOutputFormat.setOutputPath(job, new Path(args[1])); System.exit(job.waitForCompletion(true) ? 0 : 1);运行命令hadoop jar wordcount.jar WordCountDriver /input /output输出目录不能提前存在MapReduce 非常忌讳覆盖输出这是安全机制。Input 路径写的是 HDFS 路径而非本地路径我见过太多人在本地测试时直接写相对路径导致 “Input path does not exist”。4.4 自定义排序、倒排索引与分组排序的套路面试和工作里经常要求排序不是默认的字典序而是按第二列排、按数值降序排。最简单的做法是自定义 WritableComparable 作为 key。比如日志文件里每行是“时间 用户ID PV”想按 PV 降序排就写一个类实现 WritableComparable比较逻辑里先比 PVPV 相同再比用户 ID。倒排索引是搜索引擎基础数据结构。在 MapReduce 中map 阶段输出 key 是单词value 是“文档ID:次数”比如hadoop - doc1:2, doc2:3。reduce 阶段把这些文档信息合并最终得到每个单词出现在哪些文档里。核心点在于分割文本时要把当前文件名作为 value 的一部分放进 context这正好用上 FileSplit 拿文件名。分组排序Secondary Sort用得更隐蔽。比如一份数据有“订单日期 商品类目 销售额”需求是每个日期下按销售额降序排。这时可以设计复合 key“日期 销售额”设置分区器只按日期分区设置分组比较器只按日期分组排序比较器却同时按日期和销售额排序。MapReduce 默认会用同一个比较器做分区后排序和分组如果你不额外设置分组比较器分组就会严格等于分区子集内 key 的全字段导致每个销售额都是一个组期望的“组内排序”就泡汤了。这个细节点大多数新手都会踩坑。5. 三驾马车协同一次作业的完整生命周期5.1 提交前的准备数据先进 HDFS无论用哪种计算引擎输入数据必须先落到 HDFS 或能通过 HDFS 访问的位置。客户端先用hdfs dfs -put把文件上传到指定目录随后提交 jar 包。上传过程本身已经触发一次 HDFS 写流程文件按 block 切分、多副本流水线写入。你跑计算作业拿到的数据实际上引用的是分布在多台 DataNode 上的 block 集合。同时客户端会在本地生成作业的配置、依赖 jar、以及输入分片元数据job split 文件提交给 ResourceManager 时把这些信息一并交给集群。split 文件里描述了每个切分对应的文件偏移量和所在 block 的 host 列表这是 MapReduce 后续实现“数据本地性”的依据。5.2 RM 启动 AM资源申请、分配与容器拉起RM 收到提交请求后先做初始化生成 application ID把作业信息写入状态存储然后挑选一个负载适当的 NodeManager拉起 Container 并启动 ApplicationMaster 进程。AM 启动后会读取 split 信息算出需要多少个 map 任务、多少个 reduce 任务。Map 任务的数量由 split 决定一个 split 对应一个 map 任务整个作业有多少 splitmap 阶段就启动多少并行任务。Reduce 任务数量一般由用户显式设置默认是 1。这里有个实际经验如果 reduce 数量不设WordCount 会跑大量 map 但只有一个 reduce输出就只有 1 个 part 文件数据量一大会非常慢。多数生产作业会按数据规模估算 reduce 数量通常控制在 map 的 20% 到 30% 之间。AM 持续向 RM 申请容器。RM 分配后AM 联系对应 NodeManager 启动任务。真正的优化点在 map 任务调度AM 查询 split 信息里每个 split 的 host 列表优先把 map 任务调度到存有对应 block 的 DataNode 所在节点这样 map 读数据时直接走本机磁盘而不经过网络能省掉最耗时的一环。这就是“计算向数据移动”的落地形态。5.3 Map 端到 Reduce 端中间数据接力Map 任务启动后从所在节点本地或就近 DataNode 读取 split 数据逐行调用 map 方法输出 KV 进入环形缓冲区触发溢写、分区、排序最终在本地产生中间文件。Reduce 任务会持续轮询 AM 拿到的 map 任务状态发现某个 map 完成后就通过 HTTP 协议主动去拉取属于自己分区的数据。这里要看网络 IO。Reduce 拉取数据时可能同时从几十甚至上百个节点抓取所以集群内网带宽往往比磁盘更容易先成为瓶颈。为减少传输量可以在 map 端配置 Combiner 进行本地预聚合。Combiner 的逻辑必须满足交换律和结合律才能放心使用比如求和没问题求平均值就不行。Reduce 端拉完数据后还要做一次归并排序。所有数据按 key 排好后reduce 方法开始逐组处理最终把结果写回 HDFS。注意 reduce 输出默认也切分到多个 part 文件每个 reduce 写一个单独的 part-r-xxxxx 文件输出文件数量等于 reduce 数量。5.4 结果回写与资源回收所有 reduce 任务完成后AM 会向 RM 汇报作业完成同时输出总结信息包括处理记录数和耗时。用户可以在 YARN 的 Web UI 看到最终状态。随后 AM 所在 Container 被释放RM 把这次作业占用的资源全部还给集群NodeManager 也清理临时目录。这里有个小细节reduce 输出写 HDFS 时仍要走一次完整的写入流程即 NameNode 元数据注册、DataNode 流水线写入、ack 确认。因此写 HDFS 的性能和副本策略会影响作业尾延迟。生产上如果只是临时结果可以把副本数临时调低或者直接写到本地文件系统再手工合并能省不少时间。6. 实操踩坑与调优心得6.1 新手最容易踩的五个坑第一个是“Output directory already exists”。这是最经典的低级错误。MapReduce 不允许输出目录存在目的就是防止误覆盖之前任务的结果。解决办法不是强行删除保护机制而是每次运行换个新输出路径或确认结果无需保留时先hdfs dfs -rm -r清掉旧目录。第二个是“Input path does not exist”。很多人把hdfs dfs -put传完文件后直接拿本地相对路径当成输入路径跑 jar当然找不到。运行作业前先hdfs dfs -ls /your/path确认输入目录存在且能看到文件。第三个是“Container exited with a non-zero exit code 143”。这个常见于内存紧张时节点强制杀掉 Container。别只盯着应用日志先看 NodeManager 日志确认是不是物理内存或虚拟内存超限。YARN 默认按虚拟内存算限制往往要调到物理内存的 2.1 倍很多集群都是在这里翻车。第四个是“Incompatible clusterIDs”。当你从文件系统层面复制了 NameNode 的数据目录到新机器新 NameNode 会认为 data 目录不属于自己导致 DataNode 拒连。遇到这种问题确认新环境数据目录确实没历史价值后清掉 VERSION 文件里的 clusterID 并重启集群让节点重新生成兼容标记。第五个是小文件泛滥。每个 block 的元数据大约占用 NameNode 几百字节内存1000 万个 block 就会吃掉好几个 GB 内存。而且小文件多意味着 split 多map 任务数量暴增空转开销全在启动和心跳上。解决方向是合并输入或者用 Hadoop Archive 把小文件打包上游产数据时也尽量避免产生大量极小的输出文件。6.2 常用调优参数与检查命令调优前先看现状没有数据支撑的调优都是玄学。常用检查命令是hdfs dfsadmin -report yarn node -list -all yarn application -status application_id yarn logs -applicationId application_iddfsadmin -report能一眼看出版本是否有节点掉了、每节点存储空间余量、总副本数是否异常。yarn application -status能看到当前作业用的队列、AM 地址和运行状态排障第一步就靠它。内存相关的核心参数有几组yarn.nodemanager.resource.memory-mb决定单节点可分配总内存yarn.scheduler.maximum-allocation-mb决定单个 Container 上限mapreduce.map.memory.mb和mapreduce.map.java.opts决定 map 进程内存和 JVM 堆。经验是java.opts里的堆内存要略小于memory.mb因为 JVM 运行本身还需要 Metaspace 和线程栈堆设满会被 YARN 判定超用内存。block 大小dfs.blocksize需要权衡。block 太大 map 任务数少但单任务计算量大失败后重算成本高block 太小元数据膨胀和任务启动开销又压不住。顺序读的大日志场景用 256MB 起步随机读或多租户共享场景 128MB 通常更均衡。6.3 关于练习环境与学习路径的实在建议没有真实集群也能学会。单机伪分布模式下载 Hadoop 后把 HDFS 和 YARN 都开在同一台机器上足以跑通所有读写流程和 WordCount 这类作业。想更接近生产用 Docker 搭一个 3 节点的 Hadoop 集群一个 master 带两个 worker模拟节点挂掉、副本丢失、YARN 容器重试都完全够用。练习顺序建议先跑通 HDFS 命令和 fsck再手动提交一个 MapReduce 作业然后去看 YARN 的 Web UI观察 application 从提交到完成各阶段的任务变化。最后尝试修改几个参数比如调低yarn.nodemanager.resource.memory-mb触发 Container 被 kill再通过日志定位这一套走完才谈得上对“原理”有了肌肉记忆。很多在线实验平台也提供了 Hadoop 相关开箱环境比如“Hadoop 开发环境搭建及 HDFS 初体验”“MapReduce 基础编程”这类实训关卡适合用来快速验证某个 API 或命令的行为。关键是做完一个案例后把代码删了重新从空白写一遍而不是复制粘贴了事。能用思维导图把“文件上传到 HDFS 的完整链路”“作业提交到 YARN 的完整链路”默写出来你才算真正掌握。我个人在实际操作中最大的体会是调优不要一上来就改一堆参数先理解一次读写和一次作业要经过哪些节点、哪些网络跳数再针对最慢的环节下手。很多时候你以为是内存不够加了内存反而因为 GC 停顿更多而变慢你以为 reduce 并行度不够结果瓶颈在 map 端溢写太多。你要做的不是背参数而是沿着本文的几条链路一步一步看数据在哪停留、在哪等待、在哪失败。把 HDFS 的读写闭环、YARN 的容器分配、MapReduce 的 Shuffle 排序完全装进脑子里往后看 Spark、Flink 这些更年轻的框架时你会发现它们很多设计都在解决同一个问题让数据在分布式环境下流动得更稳、更快、更省。
返回列表