这是我参与更文挑战的第8天,活动详情查看: 更文挑战
背景
这是Hadoop笔记系列的第二篇,昨天把底层文件系统HDFS的笔记汇总了一下,今天整理一下MapReduce的运行机制.
Hadoop的运行机制
两类节点
- HDFS把节点分成两类:NameNode和DataNode
- MapReduce则是JobTracker节点为主,分配工作以及负责和用户程序通信
Shuffle过程
- Hadoop的shuffle过程就是从map端输出到reduce端输入之间的过程,它会占用大量的网络资源
- Shuffle描述着数据从map task输出到reduce task输入的这段过程
- Shuffle 操作,它针对多个map任务的输出按照不同的分区(Partition)通过网络复制到不同的reduce任务节点上,这个过程就称作为Shuffle
JobTracker
- JobTracker启动TaskTracker来做真正的MapReduce操作
- 一个代表客户机在单个主系统上启动的 MapReduce应用程序称为 JobTracker。类似于 NameNode,它是 Hadoop 集群中惟一负责控制 MapReduce应用程序的系统。在应用程序提交之后,将提供包含在 HDFS 中的输入和输出目录。JobTracker 使用文件块信息(物理量和位置)确定如何创建其他 TaskTracker 从属任务。MapReduce应用程序被复制到每个出现输入文件块的节点。将为特定节点上的每个文件块创建一个惟一的从属任务。每个 TaskTracker 将状态和完成信息报告给 JobTracker
- Hadoop 的这个特点非常重要,因为它并没有将存储移动到某个位置以供处理,而是将处理移动到存储
Map的操作流程
每个map task都有一个内存缓冲区,存储着map的输出结果,当缓冲区快满的时候需要将缓冲区的数据以一个临时文件的方式存放到磁盘,当整个map task结束后再对磁盘中这个map task产生的所有临时文件做合并,生成最终的正式输出文件,然后等待reduce task来拉数据
Hadoop和MapReduce的整体过程
- 客户机提交要处理的文件,NameNode会响应这个请求,并把大文件切分成多个block,传送到不同的DataNode上,并且默认有3个备份存在
- 然后应该是NameNode会把Map和Reduce程序分到到DataNode上面
- JobTracer监督每个DataNode上面的JobTracer来运行Map或Reduce程序
- Map的输入是原始文件的一个block,Map的初步输出是放到一个环形缓存中,默认100M,输出阈值是80%,等到达阈值时,一个后台spill线程会负责把在缓存中的数据写入到一个文件中; spill线程在写入磁盘前会做一些工作,首先做partition,即对key做hash,然后按照reducer的个数取模,结果就是这个key会被放到哪个reducer上面跑; 然后根据partition和key做排序,就是先整体对区(partition,或reducer编号)排序,然后对key做排序; 这个排序是MapReduce默认要执行的,感觉可能是为了后面Combiner的方便; 如果用户对排序方式有特殊要求,可以在这里指定一个定制的二次排序,比如按照Value排序; 排序完成后,若用户指定了combiner文件,就可以做一次初步的miniReduce,因为已经排序,所以只需O(n)遍历一般,把每个key做一次初步归并统计,combiner的好处是数据量少了,可以减少后续的网络IO; 排序完后,就生成一个spill文件,spill文件中包含排好序的区和key; 这些spill文件是存在本地机器的磁盘上,而不是HDFS上面(比如不会有多个备份)
- Map完成后,本地机器磁盘上有多个spill文件,此时要做一个merge操作,合并成一个文件,这里用多路归并即可; 即最后Map的输出是生成一个文件,里面是按照(区, Key)的方式排好序啦
- 然后TaskTracer通知JobTracer,然后JobTracer通知对应的Reduce来这里拉取自己partition内的数据; 也就是此时会把Map输出的那一个大文件再次拆分开成多个不同Reducer用的分块; 此时,reducer也是拉取到它自己机器的本地磁盘上,而不是放到HDFS上; 在Reducer 取走了Map 输出之后,TaskTrackers 不会立即删除这些数据,因为Reducer 可能会失败。它们会在整个作业完成后,JobTracker告知它们要删除的时候才去删除
- Reducer会依赖多个不同的Map任务,比如10个,把先拉取过来的block存到内存中,若内存足够满的时候,会将一个spill文件写入到磁盘中,此时会再次对此时内存中的已经拉取的数据做排序(此时都是同一个区,因此只对key排序即可),实际上,因为每一个block都是已经排序的,所以按理只需做归并排序即可; 此时也可以再做一次用户定制的二次排序,比如按照Value排序; 拷贝来的数据会叠加在磁盘上,有一个后台线程会将它们归并为更大的排序文件,这样做节省了后期归并的时间
- 等所有的Map输出block都到来之后,就可以为最后Reduce的输入生成数据啦; 这里并不需要生成一个最终的文件,只要在最后一次归并时总文件个数达到归并因子的数目即可; 比如最后一次归并后剩下5个中间文件,此时就无需再次归并排序,而是直接“喂”给Reduce 函数; 另一方面,这里可能既有内存中的数据,也有硬盘上的数据,综合起来,达到归并因子5个,然后用类似heap来做归并输入到Reduce中
- Reduce的输出被直接写到输出文件系统,一般是HDFS