logo资料库

Mapreduce原理.docx

第1页 / 共8页
第2页 / 共8页
第3页 / 共8页
第4页 / 共8页
第5页 / 共8页
第6页 / 共8页
第7页 / 共8页
第8页 / 共8页
资料共8页,全文预览结束
MapReduce适用场景
横向扩展和纵向扩展:
MapReduce的优化策略:
MapReduce 的数据本地化优化
HDFS的优缺点或者是使用场景和不适合的使用场景
HDFS上的块的概念和块大小
磁盘块
HDFS块
为何HDFS中的块如此之大
看文件信息
MapReduce的Shuffle过程介绍
https://blog.csdn.net/lifuxiangcaohui/article/details/22675437 很好的博客。 MapReduce 适用场景 map-reduce 计算模型适用于批处理任务,即在可接受的时间内对整个数据集计算某个 特定的查询的结果,该计算模型不适合需要实时反映数据变化状态的计算环境。 map-reduce 计算模型是以“行”为处理单位的,无法回溯已处理过的“行”,故每行日志都 必须是一个独立的语义单元,行与行之间不能有语义上的关联。 相对于传统的关系型数据库管理系统,Map-Reduce 计算模型更适合于处理半结构化或 无结构话的数据。 因为 Map-Reduce 计算模型是在处理的时候对数据进行解释的,这就意味着输入的 Key 和 Value 可以不是数据本身固有的属性,Key、Value 的选择完全取决于分析数据的人。 Map-Reduce 是一个线性可扩展模型,服务器越多,处理时间越短。 MapReduce 不是编程语言,是分布式计算框架。支持 C、Ruby、Python、Java。 优点 :易于编程、良好的扩展、高容错性、适合 PB 级的海量离线处理 缺点 :不擅长实时计算 ,毫米级返回处理结果、不擅长流式计算 MapReduce 的数据 源是静态的、不擅长 DAG 计算 map 将结果存在 hdfs 中,不适合多次从 hdfs 读写来进行计 算。 横向扩展和纵向扩展: 横向扩展 也叫 水平扩展,用更多的节点支撑更大量的请求。 如成千上万的蚂蚁完成 一项搬运工作。 纵向扩展 又叫 垂直扩展,扩展一个点的能力支撑更大的请求。如利用 1 个人的能力, 如蜘蛛侠逼停火车。 MapReduce 的优化策略: https://blog.csdn.net/aijiudu/article/details/72353510 MapReduce 的数据本地化优化 1、map 具备本地化优势策略 map 执行时优先选择存储在 HDFS 数据的服务器上执行,其次在同机架的服务器上执行, 最后在其他机架服务器上执行。 hadoop 执行第一步是将输入数据分片(分成固定大小),单个分片大小要与 HDFS 最小
数据单元相同,因为如果一个分片大于 HDFS 单元的话,就无法保证分片数据在同一台服务 器上。如果跨服务器就会增加网络传输数据的时间消耗。 2、reduce 不具备本地化优势策略 reduce 任务执行不具备就近原则,之后有个 shuffer 阶段,会通过网络将 map 执行结果 传输之 reduce 处理的服务器中,再进行计算。 HDFS 的优缺点或者是使用场景和不适 合的使用场景 1) 处理超大文件 这里的超大文件通常是指百 MB、设置数百 TB 大小的文件。目前在实际应用中,HDFS 已经能用来存储管理 PB 级的数据了。 2) 流式的访问数据 HDFS 的设计建立在更多地响应"一次写入、多次读写"任务的基础上。这意味着一个数 据集一旦由数据源生成,就会被复制分发到不同的存储节点中,然后响应各种各样的数据分 析任务请求。在多数情况下,分析任务都会涉及数据集中的大部分数据,也就是说,对 HDFS 来说,请求读取整个数据集要比读取一条记录更加高效。 3) 运行于廉价的商用机器集群上 Hadoop 设计对硬件需求比较低,只须运行在低廉的商用硬件集群上,而无需昂贵的高 可用性机器上。廉价的商用机也就意味着大型集群中出现节点故障情况的概率非常高。这就 要求设计 HDFS 时要充分考虑数据的可靠性,安全性及高可用性。 4) 不适合低延迟数据访问 如果要处理一些用户要求时间比较短的低延迟应用请求,则 HDFS 不适合。HDFS 是为了 处理大型数据集分析任务的,主要是为达到高的数据吞吐量而设计的,这就可能要求以高延 迟作为代价。 改进策略:对于那些有低延时要求的应用程序,HBase 是一个更好的选择。通过上层数 据管理项目来尽可能地弥补这个不足。 5)无法高效存储大量小文件 因为 Namenode 把文件系统的元数据放置在内存中,所以文件系统所能容纳的文件数目 是由 Namenode 的内存大小来决定。一般来说,每一个文件、文件夹和 Block 需要占据 150 字节左右的空间,所以,如果你有 100 万个文件,每一个占据一个 Block,你就至少需要 300MB 内存。当前来说,数百万的文件还是可行的,当扩展到数十亿时,对于当前的硬件水平来说 就没法实现了。还有一个问题就是,因为 Map task 的数量是由 splits 来决定的,所以用 MR 处理大量的小文件时,就会产生过多的 Maptask,线程管理开销将会增加作业时间。举个例 子,处理 10000M 的文件,若每个 split 为 1M,那就会有 10000 个 Maptasks,会有很大的线 程开销;若每个 split 为 100M,则只有 100 个 Maptasks,每个 Maptask 将会有更多的事情做, 而线程的管理开销也将减小很多。 改进策略:要想让 HDFS 能处理好小文件,有不少方法。 利用 SequenceFile、MapFile、Har 等方式归档小文件,这个方法的原理就是把小文件归 档起来管理,HBase 就是基于此的。对于这种方法,如果想找回原来的小文件内容,那就必 须得知道与归档文件的映射关系。
横向扩展,一个 Hadoop 集群能管理的小文件有限,那就把几个 Hadoop 集群拖在一个 虚拟服务器后面,形成一个大的 Hadoop 集群。google 也是这么干过的。多 Master 设计, 这个作用显而易见了。正在研发中的 GFS II 也要改为分布式多 Master 设计,还支持 Master 的 Failover,而且 Block 大小改为 1M,有意要调优处理小文件啊。附带个 Alibaba DFS 的设计, 也是多 Master 设计,它把 Metadata 的映射存储和管理分开了,由多个 Metadata 存储节点 和一个查询 Master 节点组成。 6)不支持多用户写入及任意修改文件 在 HDFS 的一个文件中只有一个写入者,而且写操作只能在文件末尾完成,即只能执行 追加操作。目前 HDFS 还不支持多个用户对同一文件的写操作,以及在文件任意位置进行修 改。 HDFS 上的块的概念和块大小 HDFS 中的块大小默认为 64MB,为什么说它如此之大,这是与磁盘块相比得出的。所 以在了解 HDFS 块之前,我们需要了解下磁盘上的块。 磁盘块 每个磁盘都有默认的数据块大小,这是磁盘进行数据读写的最小单位。构建于单个磁盘 之上的文件系统通过磁盘块来管理该文件系统中的块,该文件系统块的大小可以是磁盘块的 整数倍。文件系统块一般为几千字节,而磁盘块一般为 512 字节。文件系统块大小对于需要 读写文件的文件系统用户来说是透明的。尽管如此,系统仍然提供了一些工具(如 df 和 fsck) 来维护文件系统,它们对文件系统中的块进行操作。 HDFS 块 HDFS 块也有块的概念,但是大得多,默认为 64MB。与单一磁盘上的文件系统相似, HDFS 上的文件也被划分为块大小的多个分块(chunk),作为独立存储单元。但与其它文件 系统不同的是,HDFS 中小于一个块大小的文件不会占据整个块的空间。 为何 HDFS 中的块如此之大 HDFS 的块比磁盘块大,其目的是为了最小化寻址开销。如果块设置得足够大,从磁盘 传输数据的时间可以明显大于定位这个块开始位置所需的时间。这样,传输一个由多个块组 成的文件的时间取决于磁盘传输速率。 我们来做一个速算,如果寻址时间为 10ms 左右,而 传输速率为 100MB/s,为了使寻址时间仅占传输时间的 1%,我们需要设置块大小为 100MB 左右。而默认的块大小实际为 64MB,但是很多情况下 HDFS 使用 128MB 的块设置。以后随 着新一代磁盘驱动器传输速率的提升,块的大小将被设置得更大。 但是该参数也不会设置
得过大。MapReduce 中的 map 任务通常一次处理一个块中的数据,因此任务数太少(少于 集群中的节点数量),作业的运行速度就会比较慢。 看文件信息 hadoop fsck /user/filename 更详细的 hadoop fsck /user/filename -files -blocks -locations –racks -files 文件分块信息, -blocks 在带-files 参数后才显示 block 信息 -locations 在带-blocks 参数后才显示 block 块所在 datanode 的具体 IP 位置, -racks 在带-files 参数后显示机架位置 MapReduce 的 Shuffle 过程介绍 Shuffle 的本义是洗牌、混洗,把一组有一定规则的数据尽量转换成一组无规则的数据, 越随机越好。MapReduce 中的 Shuffle 更像是洗牌的逆过程,把一组无规则的数据尽量转换 成一组具有一定规则的数据。 为什么 MapReduce 计算模型需要 Shuffle 过程?我们都知道 MapReduce 计算模型一般包 括两个重要的阶段:Map 是映射,负责数据的过滤分发;Reduce 是规约,负责数据的计算 归并。Reduce 的数据来源于 Map,Map 的输出即是 Reduce 的输入,Reduce 需要通过 Shuffle 来获取数据。 从 Map 输出到 Reduce 输入的整个过程可以广义地称为 Shuffle。Shuffle 横跨 Map 端和 Reduce 端,在 Map 端包括 Spill 过程,在 Reduce 端包括 copy 和 sort 过程,如图所示: Spill 过程包括输出、排序、溢写、合并等步骤,如图所示:
Collect: 每个 Map 任务不断地以对的形式把数据输出到在内存中构造的一个环形数据结构中。 使用环形数据结构是为了更有效地使用内存空间,在内存中放置尽可能多的数据。 这个数据结构其实就是个字节数组,叫 Kvbuffer,名如其义,但是这里面不光放置了数 据,还放置了一些索引数据,给放置索引数据的区域起了一个 Kvmeta 的别名,在 Kvbuffer 的一块区域上穿了一个 IntBuffer(字节序采用的是平台自身的字节序)的马甲。数据区域和 索引数据区域在 Kvbuffer 中是相邻不重叠的两个区域,用一个分界点来划分两者,分界点不 是亘古不变的,而是每次 Spill 之后都会更新一次。初始的分界点是 0,数据的存储方向是向 上增长,索引数据的存储方向是向下增长,如图所示: Kvbuffer 的存放指针 bufindex 是一直闷着头地向上增长,比如 bufindex 初始值为 0,一 个 Int 型的 key 写完之后,bufindex 增长为 4,一个 Int 型的 value 写完之后,bufindex 增长 为 8。 索引是对在 kvbuffer 中的索引,是个四元组,包括:value 的起始位置、key 的起始位置、 partition 值、value 的长度,占用四个 Int 长度,Kvmeta 的存放指针 Kvindex 每次都是向下跳 四个“格子”,然后再向上一个格子一个格子地填充四元组的数据。比如 Kvindex 初始位置是 -4,当第一个写完之后,(Kvindex+0)的位置存放 value 的起始位置、(Kvindex+1)的位置存放 key 的起始位置、(Kvindex+2)的位置存放 partition 的值、(Kvindex+3)的位置存放 value 的长度, 然后 Kvindex 跳到-8 位置,等第二个和索引写完之后,Kvindex 跳到-32 位置。 Kvbuffer 的大小虽然可以通过参数设置,但是总共就那么大,和索引不断地增加,加着 加着,Kvbuffer 总有不够用的那天,那怎么办?把数据从内存刷到磁盘上再接着往内存写数 据,把 Kvbuffer 中的数据刷到磁盘上的过程就叫 Spill,多么明了的叫法,内存中的数据满了 就自动地 spill 到具有更大空间的磁盘。 关于 Spill 触发的条件,也就是 Kvbuffer 用到什么程度开始 Spill,还是要讲究一下的。如 果把 Kvbuffer 用得死死得,一点缝都不剩的时候再开始 Spill,那 Map 任务就需要等 Spill 完 成腾出空间之后才能继续写数据;如果 Kvbuffer 只是满到一定程度,比如 80%的时候就开始 Spill,那在 Spill 的同时,Map 任务还能继续写数据,如果 Spill 够快,Map 可能都不需要为 空闲空间而发愁。两利相衡取其大,一般选择后者。
Spill 这个重要的过程是由 Spill 线程承担,Spill 线程从 Map 任务接到“命令”之后就开始 正式干活,干的活叫 SortAndSpill,原来不仅仅是 Spill,在 Spill 之前还有个颇具争议性的 Sort。 Sort 先把 Kvbuffer 中的数据按照 partition 值和 key 两个关键字升序排序,移动的只是索引数 据,排序结果是 Kvmeta 中数据按照 partition 为单位聚集在一起,同一 partition 内的按照 key 有序。 Spill Spill 线程为这次 Spill 过程创建一个磁盘文件:从所有的本地目录中轮训查找能存储这 么大空间的目录,找到之后在其中创建一个类似于“spill12.out”的文件。Spill 线程根据排过序 的 Kvmeta 挨个 partition 的把数据吐到这个文件中,一个 partition 对应的数据吐完之后顺序 地吐下个 partition,直到把所有的 partition 遍历完。一个 partition 在文件中对应的数据也叫 段(segment)。 所有的 partition 对应的数据都放在这个文件里,虽然是顺序存放的,但是怎么直接知道 某个 partition 在这个文件中存放的起始位置呢?强大的索引又出场了。有一个三元组记录某 个 partition 对应的数据在这个文件中的索引:起始位置、原始数据长度、压缩之后的数据长 度,一个 partition 对应一个三元组。然后把这些索引信息存放在内存中,如果内存中放不下 了,后续的索引信息就需要写到磁盘文件中了:从所有的本地目录中轮训查找能存储这么大 空间的目录,找到之后在其中创建一个类似于“spill12.out.index”的文件,文件中不光存储了 索引数据,还存储了 crc32 的校验数据。(spill12.out.index 不一定在磁盘上创建,如果内存(默 认 1M 空间)中能放得下就放在内存中,即使在磁盘上创建了,和 spill12.out 文件也不一定 在同一个目录下。) 每一次 Spill 过程就会最少生成一个 out 文件,有时还会生成 index 文件,Spill 的次数也 烙印在文件名中。索引文件和数据文件的对应关系如下图所示: 在 Spill 线程如火如荼的进行 SortAndSpill 工作的同时,Map 任务不会因此而停歇,而是 一无既往地进行着数据输出。Map 还是把数据写到 kvbuffer 中,那问题就来了:只顾着闷头 按照 bufindex 指针向上增长,kvmeta 只顾着按照 Kvindex 向下增长,是保持指针起始位置不
变继续跑呢,还是另谋它路?如果保持指针起始位置不变,很快 bufindex 和 Kvindex 就碰头 了,碰头之后再重新开始或者移动内存都比较麻烦,不可取。Map 取 kvbuffer 中剩余空间的 中间位置,用这个位置设置为新的分界点,bufindex 指针移动到这个分界点,Kvindex 移动 到这个分界点的-16 位置,然后两者就可以和谐地按照自己既定的轨迹放置数据了,当 Spill 完成,空间腾出之后,不需要做任何改动继续前进。分界点的转换如下图所示: Map 任务总要把输出的数据写到磁盘上,即使输出数据量很小在内存中全部能装得下, 在最后也会把数据刷到磁盘上。 Merge Map 任务如果输出数据量很大,可能会进行好几次 Spill,out 文件和 Index 文件会产生 很多,分布在不同的磁盘上。最后把这些文件进行合并的 merge 过程闪亮登场。 Merge 过程怎么知道产生的 Spill 文件都在哪了呢?从所有的本地目录上扫描得到产生 的 Spill 文件,然后把路径存储在一个数组里。Merge 过程又怎么知道 Spill 的索引信息呢? 没错,也是从所有的本地目录上扫描得到 Index 文件,然后把索引信息存储在一个列表里。 到这里,又遇到了一个值得纳闷的地方。在之前 Spill 过程中的时候为什么不直接把这些信 息存储在内存中呢,何必又多了这步扫描的操作?特别是 Spill 的索引数据,之前当内存超 限之后就把数据写到磁盘,现在又要从磁盘把这些数据读出来,还是需要装到更多的内存中。 之所以多此一举,是因为这时 kvbuffer 这个内存大户已经不再使用可以回收,有内存空间来 装这些数据了。(对于内存空间较大的土豪来说,用内存来省却这两个 io 步骤还是值得考虑 的。) 然后为 merge 过程创建一个叫 file.out 的文件和一个叫 file.out.Index 的文件用来存储最 终的输出和索引。 一个 partition 一个 partition 的进行合并输出。对于某个 partition 来说,从索引列表中 查询这个 partition 对应的所有索引信息,每个对应一个段插入到段列表中。也就是这个 partition 对应一个段列表,记录所有的 Spill 文件中对应的这个 partition 那段数据的文件名、 起始位置、长度等等。 然后对这个 partition 对应的所有的 segment 进行合并,目标是合并成一个 segment。当 这个 partition 对应很多个 segment 时,会分批地进行合并:先从 segment 列表中把第一批取
出来,以 key 为关键字放置成最小堆,然后从最小堆中每次取出最小的输出到一个临时文件 中,这样就把这一批段合并成一个临时的段,把它加回到 segment 列表中;再从 segment 列表中把第二批取出来合并输出到一个临时 segment,把其加入到列表中;这样往复执行, 直到剩下的段是一批,输出到最终的文件中。 最终的索引数据仍然输出到 Index 文件中。 Map 端的 Shuffle 过程到此结束。 Copy Reduce 任务通过 HTTP 向各个 Map 任务拖取它所需要的数据。每个节点都会启动一个 常驻的 HTTP server,其中一项服务就是响应 Reduce 拖取 Map 数据。当有 MapOutput 的 HTTP 请求过来的时候,HTTP server 就读取相应的 Map 输出文件中对应这个 Reduce 部分的数据通 过网络流输出给 Reduce。 Reduce 任务拖取某个 Map 对应的数据,如果在内存中能放得下这次数据的话就直接把 数据写到内存中。Reduce 要向每个 Map 去拖取数据,在内存中每个 Map 对应一块数据,当 内存中存储的 Map 数据占用空间达到一定程度的时候,开始启动内存中 merge,把内存中 的数据 merge 输出到磁盘上一个文件中。 如果在内存中不能放得下这个 Map 的数据的话,直接把 Map 数据写到磁盘上,在本地 目录创建一个文件,从 HTTP 流中读取数据然后写到磁盘,使用的缓存区大小是 64K。拖一 个 Map 数据过来就会创建一个文件,当文件数量达到一定阈值时,开始启动磁盘文件 merge, 把这些文件合并输出到一个文件。 有些 Map 的数据较小是可以放在内存中的,有些 Map 的数据较大需要放在磁盘上,这 样最后 Reduce 任务拖过来的数据有些放在内存中了有些放在磁盘上,最后会对这些来一个 全局合并。 Merge Sort 这里使用的 Merge 和 Map 端使用的 Merge 过程一样。Map 的输出数据已经是有序的, Merge 进行一次合并排序,所谓 Reduce 端的 sort 过程就是这个合并的过程。一般 Reduce 是一边 copy 一边 sort,即 copy 和 sort 两个阶段是重叠而不是完全分开的。 Reduce 端的 Shuffle 过程至此结束。
分享到:
收藏