首页 / 资讯中心 / 文章详情

Hadoop核心机制解析:从HDFS到YARN的分布式存储与计算实践

Hadoop核心机制解析:从HDFS到YARN的分布式存储与计算实践 ★ FEATURED ARTICLE
1. 从一个“数据装不下”的真实困境说起我第一次真正意识到需要Hadoop是在处理一批日志数据的时候。当时手头有一台配置还算不错的服务器内存64GB硬盘4TB自认为“够用了”。结果数据量一上来单是原始日志就超过了8TB而且每天还在以几百GB的速度增长。更麻烦的是这些数据不是简单地存起来就行还要做清洗、聚合、统计分析。单机跑一个脚本光读取就花了几个小时中间一旦内存溢出整个任务就得从头再来。那时候我试过垂直扩展的思路——加内存、换更快的硬盘、优化代码。短期确实有效但成本增长曲线太陡了。从64GB加到256GB内存价格翻了好几倍而数据量增长却是线性的、持续的。更关键的是单机的可靠性问题没有解决硬盘坏了怎么办任务跑到一半机器宕机怎么办这些问题不是靠“买更好的硬件”就能绕过去的。Hadoop就是在这个背景下进入我的视野的。它的核心思路很朴素既然一台机器搞不定那就用一群普通机器来分担。这个思路听起来简单但真正落地需要解决一系列问题——数据怎么分布计算怎么调度某台机器挂了怎么保证任务继续这些问题的答案构成了Hadoop整个技术体系的基础。这篇文章适合那些正在面对数据量增长、单机处理越来越吃力、想了解分布式存储与计算到底怎么回事的从业者。我不会堆砌概念而是从实际使用角度把Hadoop的核心机制、组件分工、部署要点和常见坑讲清楚。即使你之前只听过“大数据”这个词也能看懂它到底在解决什么问题。2. Hadoop到底解决了哪几类问题2.1 存储问题单机硬盘装不下也读不快Hadoop的第一个核心能力是分布式存储对应组件叫HDFSHadoop Distributed File System。它的基本逻辑是把一个大文件切成固定大小的数据块默认128MB然后把这些块分散存到集群中不同的机器上。每台机器只存一部分合起来就是完整的数据。这样做的好处很直接。假设你有10TB数据集群有10台机器每台只需要存1TB左右。如果以后数据涨到20TB加机器就行不需要换掉现有设备。读取的时候多台机器可以同时提供数据吞吐量是单机的数倍甚至数十倍。但这里有个关键设计HDFS默认会把每个数据块存三份分别放在不同机器上。这意味着实际存储开销是原始数据的三倍。很多人第一次听到这个会觉得“太浪费了”但这是为了保证可靠性。普通商用硬盘的故障率并不低三副本机制让系统在任意一到两台机器同时故障时数据仍然可用。这是用存储成本换可靠性和可用性在实际生产中是划算的。2.2 计算问题单机算得慢还容易中途失败有了分布式存储接下来就是分布式计算对应组件是MapReduce。它的核心思想是把一个大计算任务拆成很多小任务分发到多台机器上并行执行最后再把结果汇总。MapReduce的编程模型只有两个主要阶段Map和Reduce。Map阶段负责“分解”把原始数据映射成键值对Reduce阶段负责“汇总”把相同键的值合并处理。中间还有一个Shuffle阶段负责把Map的输出按照键重新分发到不同的Reduce任务上。这个模型看起来简单但威力在于你只需要写Map和Reduce的逻辑至于任务怎么切分、怎么调度、某台机器失败了怎么重试框架全部帮你处理。这是Hadoop对开发者最大的价值——把分布式编程的复杂性封装起来让普通人也能写出能在集群上跑的程序。2.3 资源管理问题多任务怎么共享一个集群早期Hadoop中MapReduce既负责计算也负责资源调度导致集群只能跑MapReduce任务。后来引入了YARNYet Another Resource Negotiator把资源管理和计算框架分离开。YARN负责管理集群的CPU、内存等资源任何计算框架MapReduce、Spark、Flink等都可以向YARN申请资源来运行自己的任务。这个变化的意义很大。它让一个Hadoop集群可以同时运行多种类型的计算任务提高了集群利用率。比如白天跑批处理分析晚上跑机器学习训练资源统一由YARN调配不需要为每种任务单独维护一套集群。2.4 三类问题的关系与边界HDFS、MapReduce、YARN这三者构成了Hadoop的核心。HDFS解决“数据放哪”MapReduce解决“怎么算”YARN解决“谁来分配资源”。它们之间的关系可以用一个简单类比理解HDFS是仓库MapReduce是流水线YARN是调度中心。但需要明确的是Hadoop并不是万能的。它擅长的是大规模批处理场景——数据量大、对延迟不敏感、任务可以分解并行。如果你需要毫秒级响应的实时查询Hadoop并不合适那是内存数据库或流处理框架的领域。理解这个边界比盲目上Hadoop更重要。3. HDFS的读写流程与副本机制拆解3.1 一次写入请求经历了什么当客户端要往HDFS写一个文件时背后发生的事情比想象中复杂。客户端首先联系NameNodeHDFS的主节点请求创建文件。NameNode会检查权限、确认文件不存在然后在元数据中记录这个新文件但此时并不分配具体的数据块。客户端开始写数据时会把文件切分成一个个数据块。每写一个块客户端向NameNode申请该块应该存到哪些DataNode上。NameNode根据副本放置策略返回一组DataNode列表。默认策略是第一个副本放在客户端所在机器如果客户端在集群内第二个副本放在不同机架的机器上第三个副本放在与第二个副本同机架但不同机器上。客户端拿到列表后开始向第一个DataNode传输数据。第一个DataNode收到数据后会同时转发给第二个DataNode第二个再转发给第三个。这种流水线式的传输方式比客户端分别向三个节点发送效率更高因为利用了每台机器的网络带宽。注意如果写入过程中某个DataNode失败客户端会通知NameNodeNameNode会安排新的DataNode继续完成副本写入。整个过程对客户端是透明的不需要手动干预。3.2 读取时如何找到数据读取流程相对简单一些。客户端向NameNode请求读取某个文件NameNode返回该文件所有数据块的位置信息每个块对应的DataNode列表。客户端拿到列表后会优先选择距离自己最近的DataNode读取数据。如果某个DataNode读取失败客户端会自动切换到下一个副本所在的DataNode。这里有个实际使用中的经验HDFS的读取性能很大程度上取决于客户端与DataNode的网络距离。如果客户端在集群外所有读取都要走网络速度会明显慢于集群内读取。所以在实际部署中计算任务尽量和存储放在同一个集群内利用数据本地性Data Locality来减少网络传输。3.3 副本机制背后的权衡三副本是HDFS的默认配置但不是唯一选择。你可以根据数据的重要程度调整副本数。比如临时中间结果可以设为1副本节省存储空间核心业务数据可以设为3副本甚至更高。但副本数不是越多越好。每增加一个副本写入时需要传输的数据量就增加一份存储开销也线性增长。在实际集群中三副本是一个平衡点既能容忍一定数量的机器故障又不会让存储成本失控。还有一个容易被忽略的点副本的放置策略直接影响可靠性。如果所有副本都放在同一个机架上一旦机架断电或交换机故障数据就不可用了。HDFS的默认策略通过跨机架放置来避免这个问题但在小规模集群中比如只有几台机器机架感知可能配置不完整需要额外注意。3.4 NameNode的单点问题与解决方案NameNode是HDFS的核心它存储了整个文件系统的元数据文件名、目录结构、块位置等。如果NameNode挂了整个HDFS就无法访问。这是HDFS架构中最大的单点风险。Hadoop提供了两种应对方案。第一种是SecondaryNameNode它定期合并NameNode的编辑日志和镜像文件减轻NameNode的启动压力。但要注意SecondaryNameNode并不是热备它不能自动接管NameNode的工作。第二种是NameNode HAHigh Availability通过配置两个NameNode一个Active一个Standby来实现自动故障切换。Standby NameNode实时同步Active的元数据变化当Active故障时Standby可以快速接管。这是生产环境中的标准做法。提示即使配置了HANameNode的元数据仍然需要定期备份。元数据丢失意味着整个文件系统的数据都无法访问这是灾难性的。4. MapReduce编程模型的实际运作细节4.1 一个WordCount程序的完整执行链路WordCount是MapReduce的“Hello World”但它的执行过程能说明很多问题。假设你要统计一个10GB文本文件中每个单词出现的次数。首先Hadoop会把输入文件切分成多个分片InputSplit每个分片默认对应一个HDFS数据块。每个分片会分配给一个Map任务。Map任务逐行读取分片内容对每一行按空格切分单词然后输出键值对单词, 1。这些键值对不会立即写到磁盘而是先存在内存缓冲区中。当缓冲区达到一定阈值默认80%会触发溢写Spill把数据写到本地磁盘。在溢写之前会按照键进行排序和分区。分区决定了这个键值对最终会被哪个Reduce任务处理。所有Map任务完成后进入Shuffle阶段。每个Reduce任务会从所有Map任务的输出中拉取属于自己分区的数据。这个过程涉及大量的网络传输和磁盘I/O是MapReduce中最耗时的阶段之一。Reduce任务拿到数据后会按照键进行归并排序然后对每个键的值列表执行Reduce逻辑。在WordCount中就是把同一个单词的所有计数相加输出单词, 总次数。4.2 为什么Shuffle是性能瓶颈Shuffle之所以慢是因为它同时涉及网络传输、磁盘读写和排序。Map输出的数据要先写到本地磁盘然后Reduce再通过网络拉取最后还要在Reduce端做归并排序。每一步都有I/O开销。在实际调优中有几个参数值得关注。mapreduce.task.io.sort.mb控制Map端排序缓冲区大小默认100MB。如果Map输出数据量大适当调大这个值可以减少溢写次数。mapreduce.reduce.shuffle.parallelcopies控制Reduce端并行拉取数据的线程数默认5在网络带宽充足的情况下可以调大。还有一个经验如果Map输出数据量远大于输入数据量比如做笛卡尔积之类的操作Shuffle会成为严重瓶颈。这时候需要考虑是否真的适合用MapReduce或者是否可以在Map端做更多预聚合来减少Shuffle数据量。4.3 Combiner一个容易被忽视的优化点Combiner是MapReduce中的一个可选组件它在Map端对输出进行局部聚合。还是以WordCount为例如果某个Map任务处理的分片中“hello”出现了100次没有Combiner的话会输出100个hello, 1有Combiner的话会先在Map端合并成hello, 100只传输一个键值对。Combiner的使用有一个前提它的逻辑必须满足交换律和结合律。也就是说先合并再计算和先计算再合并的结果必须一致。求和、求最大值都满足这个条件但求平均值就不行。在实际使用中Combiner能显著减少Shuffle数据量但很多人会忘记配置它。如果你的Map输出数据量很大而Reduce逻辑又支持局部聚合一定要加上Combiner。4.4 任务失败与推测执行在分布式环境中任务失败是常态而不是异常。MapReduce框架对任务失败有完整的处理机制。如果某个Map或Reduce任务失败框架会在另一台机器上重新调度这个任务。默认情况下一个任务最多可以重试4次。但有一种情况更棘手任务没有失败但执行得特别慢。这可能是因为机器负载高、磁盘有问题、或者网络拥塞。MapReduce引入了推测执行Speculative Execution机制如果框架发现某个任务明显慢于同批次的其他任务会在另一台机器上启动一个相同的任务谁先完成就用谁的结果。推测执行在大多数情况下能缩短作业总时间但也不是没有代价。它会占用额外的资源如果集群资源紧张反而可能拖慢其他任务。在实际调优中可以根据集群负载情况决定是否开启推测执行。5. YARN如何调度集群资源5.1 ResourceManager与NodeManager的分工YARN采用主从架构。ResourceManager是主节点负责整个集群的资源管理和分配。NodeManager是从节点运行在每台机器上负责管理本机的资源CPU、内存等和容器Container的生命周期。当客户端提交一个应用程序时ResourceManager会分配一个容器来运行ApplicationMaster。ApplicationMaster负责这个应用程序的具体执行向ResourceManager申请资源、与NodeManager通信启动任务、监控任务状态。这种架构的好处是ResourceManager只负责宏观调度具体的任务管理由ApplicationMaster负责。不同的计算框架可以实现自己的ApplicationMaster从而在YARN上运行。5.2 调度器的选择与影响YARN提供了三种主要调度器FIFO、Capacity和Fair。FIFO最简单先提交的作业先获得资源但大作业会阻塞后面的小作业。Capacity调度器把集群资源划分成多个队列每个队列有容量保证适合多租户场景。Fair调度器动态调整资源分配让所有作业公平地获得资源。在实际生产环境中Capacity和Fair更常用。选择哪种取决于你的业务特点。如果多个团队共享集群且每个团队有明确的资源预算Capacity更合适。如果希望作业之间公平竞争Fair更合适。注意调度器的配置直接影响作业的等待时间和集群利用率。在集群资源紧张时不合理的队列配置可能导致某些作业长期得不到资源。5.3 内存与CPU的资源配置YARN管理的最主要资源是内存和CPU。内存资源以MB为单位CPU资源以虚拟核vCore为单位。每个NodeManager会报告本机可用的内存和vCore数量。在配置时需要预留一部分内存给操作系统和Hadoop自身进程。比如一台128GB内存的机器可能只报告120GB给YARN使用。每个容器需要指定内存大小如果容器实际使用内存超过申请值NodeManager会杀掉这个容器。CPU资源的调度相对宽松一些。默认情况下vCore只是逻辑上的限制并不做硬性隔离。也就是说一个容器可以使用超过其申请vCore数量的CPU时间。如果需要严格的CPU隔离需要配置Linux的cgroups。5.4 一个实际集群的资源规划案例假设你有一个10台机器的集群每台机器配置为32核CPU、128GB内存、4TB硬盘。那么整个集群的资源总量是320个vCore、1280GB内存、40TB存储。但实际可用资源要打折扣。每台机器需要预留约8GB内存给系统所以YARN可用内存约120GB每台总共1200GB。vCore方面如果每个vCore对应一个物理核总共320个。但实际配置中通常会设置vCore数略多于物理核数比如1.5倍以提高CPU利用率。存储方面HDFS可用空间大约是总硬盘容量的70%到80%因为要预留空间给临时文件、日志和副本开销。40TB的原始容量实际可用可能在28TB到32TB之间。这个规划不是一成不变的。随着业务变化可能需要调整队列容量、增加机器、或者优化任务资源申请。关键是持续监控集群利用率根据实际数据做调整。6. 从零搭建Hadoop集群的关键步骤与避坑点6.1 环境准备中最容易忽略的细节搭建Hadoop集群的第一步是准备机器和环境。很多人会直接开始安装Hadoop但忽略了几个基础配置导致后面问题不断。首先是主机名和hosts文件。集群中每台机器必须有唯一的主机名并且所有机器都能通过主机名互相访问。我见过因为hosts文件配置不一致导致DataNode无法注册到NameNode的情况。排查了半天才发现是某台机器的hosts文件少了一行。其次是SSH免密登录。Hadoop的启动脚本需要通过SSH在集群各机器上执行命令。如果没配置免密每次启动都要输入密码自动化脚本就无法运行。配置方法是生成密钥对然后把公钥追加到所有机器的authorized_keys文件中。还有时间同步。集群中各机器的时间差不能太大否则会导致各种奇怪的问题比如任务调度异常、日志时间错乱。建议配置NTP服务保持时间同步。6.2 核心配置文件的参数含义Hadoop的配置主要集中在几个XML文件中。core-site.xml配置通用参数最重要的是fs.defaultFS指定HDFS的访问地址。hdfs-site.xml配置HDFS相关参数包括副本数、NameNode和DataNode的数据目录等。yarn-site.xml配置YARN相关参数。mapred-site.xml配置MapReduce相关参数。在配置时有几个参数需要特别注意。dfs.replication控制副本数默认3。在小规模集群中比如3台机器如果还是3副本存储利用率会很低可以考虑设为2。dfs.namenode.name.dir和dfs.datanode.data.dir指定数据存储路径建议配置多个路径分别挂载不同磁盘提高I/O吞吐。yarn.nodemanager.resource.memory-mb指定NodeManager可用的总内存这个值要根据机器实际内存减去系统预留来设置。设得太大可能导致系统内存不足设得太小则浪费资源。6.3 格式化与启动的顺序问题第一次启动Hadoop前需要格式化NameNode。命令是hdfs namenode -format。这个操作会创建NameNode的元数据目录。注意格式化只能执行一次重复格式化会导致元数据丢失集群中的数据就无法访问了。启动顺序也有讲究。先启动HDFSstart-dfs.sh再启动YARNstart-yarn.sh。停止时顺序相反先停YARN再停HDFS。如果顺序反了可能会导致某些进程无法正常停止。启动后可以通过Web UI检查集群状态。NameNode的Web UI默认端口是9870ResourceManager的默认端口是8088。通过这些界面可以查看集群节点数、存储容量、运行中的任务等信息。6.4 常见故障与排查思路集群搭建过程中最常见的问题是DataNode无法启动或无法注册到NameNode。排查思路是先看日志DataNode的日志通常在logs目录下文件名包含datanode。常见原因包括防火墙阻止了通信、hosts文件配置错误、数据目录权限不对、多次格式化导致clusterID不一致。另一个常见问题是YARN的NodeManager无法启动。检查yarn-site.xml中yarn.nodemanager.aux-services是否配置正确通常是mapreduce_shuffle。还要检查NodeManager的内存配置是否超过了机器实际可用内存。如果任务提交后一直处于ACCEPTED状态说明资源不足无法分配容器。这时候需要检查集群资源使用情况或者调整队列配置和任务资源申请。提示在排查问题时日志是最好的朋友。不要只看表面现象要深入日志找到根本原因。很多问题在日志中都有明确的错误信息只是需要耐心查找。7. 实际使用中的性能调优经验7.1 数据本地性对性能的影响数据本地性是指计算任务尽量在存储有该任务所需数据的机器上执行。在Hadoop中如果一个Map任务读取的数据块就在本机就不需要走网络传输速度会快很多。Hadoop会尽量满足数据本地性但不是总能做到。如果本机资源不足任务可能被调度到其他机器上变成机架内传输甚至跨机架传输。跨机架传输的延迟和带宽成本都更高。在实际使用中可以通过增加集群规模、合理配置资源来提高数据本地性命中率。另外如果数据是从外部导入的尽量在导入时就分布到集群各节点而不是集中在一台机器上。7.2 小文件问题的处理策略HDFS设计时假设文件都很大因为大文件更容易管理元数据开销也小。但实际使用中经常会有大量小文件比如每个日志文件只有几KB。这些小文件会给NameNode带来巨大压力因为每个文件、每个块都需要在NameNode内存中存储元数据。处理小文件有几种常见策略。第一种是在数据进入HDFS之前就合并比如用Flume或Logstash把多个小文件合并成大文件。第二种是用Hadoop的归档工具HAR把小文件打包。第三种是在MapReduce任务中使用CombineFileInputFormat把多个小文件合并成一个分片处理。我个人的经验是最好在数据源头就控制文件大小。如果源头无法控制就在进入HDFS的环节做合并。事后处理小文件往往更麻烦而且效果不一定好。7.3 内存与GC调优的实操建议Hadoop各组件的默认内存配置通常偏保守在生产环境中需要根据实际情况调整。NameNode的内存主要消耗在元数据上每个文件、每个块大约占用150字节。如果有1亿个文件每个文件平均1个块NameNode大约需要15GB内存来存储元数据。DataNode的内存消耗相对较小主要受并发读写线程数影响。MapReduce任务的内存配置更复杂需要分别设置Map任务和Reduce任务的堆大小。mapreduce.map.java.opts和mapreduce.reduce.java.opts控制JVM堆大小mapreduce.map.memory.mb和mapreduce.reduce.memory.mb控制容器内存。GC调优方面建议使用G1垃圾收集器它在大堆内存下表现更好。可以通过-XX:UseG1GC开启。另外设置合理的-XX:MaxGCPauseMillis可以控制GC停顿时间但设得太小会导致GC更频繁需要根据实际负载做权衡。7.4 压缩格式的选择与权衡在Hadoop中使用压缩可以显著减少存储空间和网络传输量但会增加CPU开销。选择压缩格式时需要在压缩比、压缩速度和解压速度之间做权衡。常见的压缩格式有Snappy、LZO、Gzip和Bzip2。Snappy压缩和解压速度都很快但压缩比一般适合中间数据。Gzip压缩比高但速度较慢适合冷数据存储。Bzip2压缩比最高但速度最慢且不支持分片在MapReduce中使用受限。在实际使用中我通常这样选择Map输出用Snappy因为Shuffle阶段对速度敏感最终存储用Gzip因为存储成本更重要如果数据需要分片处理用LZO或Snappy它们支持分片。注意压缩格式的选择不是孤立的要考虑整个数据处理链路。如果上游用了一种格式下游可能也需要支持解压否则就要增加解压步骤。8. 一些个人体会与后续可扩展的方向Hadoop的生态在不断发展但它的核心思想——用普通机器集群解决大规模数据问题——一直没有变。我在实际使用中最大的体会是Hadoop不是银弹它有自己的适用边界。对于批处理场景它依然是最成熟、最稳定的选择之一但对于实时性要求高的场景需要结合其他框架来使用。另外Hadoop的运维成本不低。集群规模越大故障概率越高对运维人员的要求也越高。如果团队没有足够的运维能力可以考虑使用云上的托管Hadoop服务把底层运维交给云厂商自己专注于业务逻辑。从学习路径来看建议先理解HDFS和MapReduce的基本原理再动手搭建一个小集群跑通WordCount然后逐步深入YARN的资源管理和调优。不要一开始就追求大而全先把核心链路走通再根据实际需求扩展。后续可以扩展的方向包括Hadoop与Spark的集成、Hive数据仓库的使用、HBase列式存储的应用等。这些都是在Hadoop基础上构建的上层工具解决了不同场景下的问题。但无论上层工具怎么变底层的HDFS和YARN依然是基石理解它们的工作原理对用好整个生态至关重要。
阅读完成 · 觉得有帮助?
咨询建站