hadoop在执行MapReduce任务时,在map阶段,map函数产生的输出,并不是直接写入磁盘的。为了提高效率,它将输出结果先写入到内存中(即环形内存缓冲区,默认大小100M),再从缓冲区(溢)写入磁盘。
下面我们就来看看这段代码。
1、找到环形内存缓冲区
在运行job时,有条输出:
09/04/07 12:34:35 INFO mapred.MapTask: io.sort.mb = 100
上面的io.sort.mb,即map环形内存缓冲区的大小。
在org.apache.hadoop.mapred.MapTask中的第764行找到“io.sort.mb”
第781行:
kvbuffer是在第715行定义的:
看,这个内存缓冲区竟然是个byte数组!!
2、什么时候溢写到磁盘的?
1) 溢写比设置了<1的值,并且该值到了的时候
2) 溢写比为1,缓存满了的时候
3、缓冲区怎么成环形的?
答:通过折行。
----------------------------------
“折行写”
----------------------------------
“折行写”后的reset
4、“成环”示意图
上面的代码一定看的眼花缭乱吧?呵呵,我一开始看的时候,也被弄得很糊涂。请看下面的示意图,就会对这个环形缓冲有个好的理解了。
5、“溢写”过程
即:
溢写完毕后,原来的bufmark变成了bufstart
6、缓存为什么要设计成环形的?有什么好处?
答:使输入输出并行工作,即“写缓冲”可以和“溢写”并行。“溢写”工作由单独的线程来做。
*** THE END***
下面我们就来看看这段代码。
1、找到环形内存缓冲区
在运行job时,有条输出:
09/04/07 12:34:35 INFO mapred.MapTask: io.sort.mb = 100
上面的io.sort.mb,即map环形内存缓冲区的大小。
在org.apache.hadoop.mapred.MapTask中的第764行找到“io.sort.mb”
第781行:
kvbuffer = new byte[maxMemUsage - recordCapacity];
kvbuffer是在第715行定义的:
private byte[] kvbuffer; // main output buffer
看,这个内存缓冲区竟然是个byte数组!!
2、什么时候溢写到磁盘的?
第762行: final float spillper = job.getFloat("io.sort.spill.percent",(float)0.8); // 溢写比:默认是0.8,就是说,缓存区80%满了的时候,就要将数据从内存溢写到磁盘了 这100M,还分成2块:数据缓存和记录缓存 第707行: private final int[] kvoffsets; // indices into kvindices // 这个int型的数组就是记录缓存 第941行: // sort by key return comparator.compare(kvbuffer, kvindices[ii + KEYSTART], kvindices[ii + VALSTART] - kvindices[ii + KEYSTART], kvbuffer, kvindices[ij + KEYSTART], kvindices[ij + VALSTART] - kvindices[ij + KEYSTART]); // 在内存缓冲区中按key进行排序综上,溢写发生在:
1) 溢写比设置了<1的值,并且该值到了的时候
2) 溢写比为1,缓存满了的时候
3、缓冲区怎么成环形的?
答:通过折行。
----------------------------------
“折行写”
第1038行: boolean buffull = false; // 缓存是否满了 // 这里的“满”,有2种情况: 1) bufindex + len > bufvoid // 就是说,达到了末尾 // 但是这种情况,可能不是真的满了 // 因为,在数组0-bufstart之间,可能还有很大的空置空间 2) bufindex + len > bufstart // 由于缓冲区已经成“环”,这种情况,是真的满了。 第1039行: boolean wrap = false; // 是否需要“折行写” // “折行写”的条件: 1) bufstart <= bufend && bufend <= bufindex // buffer是一段连续的区域,还没有形成“环” 2) (bufvoid - bufindex) + bufstart > len // 数组末尾,加上数组开头的空间能够存储当前数据 // 真正执行“折行写”的代码(Line1101-1107): if (buffull) { // 这里满足上面第1种buffull = true的条件,否则 // 将先溢写至磁盘后,再到达这里。 final int gaplen = bufvoid - bufindex; System.arraycopy(b, off, kvbuffer, bufindex, gaplen); len -= gaplen; off += gaplen; bufindex = 0; }
----------------------------------
“折行写”后的reset
Line996-1014的reset()方法: 这个方法被调用的地点:第895行, 第893行,在collect方法中: if (bufindex < keystart) { // wrapped the key; reset required bb.reset(); keystart = 0; } // 如果key被“折写”成2段,则reset缓冲区 // 这时候:一个key有一半写在了数组末尾,另一半写在了数组列头时候 这个方法被调用的时候(bufindex < keystart == true): 一定是,序列化后的key被写入缓存区,而且是被wrap(折行)写入的! 这个方法里的解释: protected synchronized void reset() throws IOException { // spillLock unnecessary; If spill wraps, then // bufindex < bufstart < bufend so contention is impossible // a stale value for bufstart does not affect correctness, since // we can only get false negatives that force the more // conservative path int headbytelen = bufvoid - bufmark; // headbytelen的意思是:被“折行”写入的key的前段部分 // bufvoid是缓冲区的右边界 // bufmark是缓冲区中上次存值后的右边界 // bufvoid - bufmark :就是被“折行”写入的key的前半段 bufvoid = bufmark; if (bufindex + headbytelen < bufstart) { // 基本上这个条件成立的可能性比较大, // 它的意思是说:整个key的长度(前半段的长度是headbytelen,存在缓冲区最后面;后半段的长度是bufindex,存在缓冲区的最前面)在bufstart之间的空间能存得下 // 那么接下来的2行代码:把这个key的两端,都移到缓冲区的最前面! System.arraycopy(kvbuffer, 0, kvbuffer, headbytelen, bufindex); System.arraycopy(kvbuffer, bufvoid, kvbuffer, 0, headbytelen); bufindex += headbytelen; } else { // 这种情况真的很难达到:要求溢写比(io.sort.spill.percent)为1,并且bufstart很靠近0的时候 // 这种情况是:buffer真的很满了(bufstart-bufindex<headbytelen),以至于在bufstart之前的空间不足以存储一个key byte[] keytmp = new byte[bufindex]; System.arraycopy(kvbuffer, 0, keytmp, 0, bufindex); bufindex = 0; // 把这个key分2次写入out // 也就是说: // 1) 这个key,先从kvbuffer缓存中删除 // 2) 接下来,应该是将缓存中数据溢写到磁盘上 // 3) out中的这个key再次写入清空后的缓存里! // 估计在清空缓存前,这个都会被阻塞。 out.write(kvbuffer, bufmark, headbytelen); out.write(keytmp); } } }
4、“成环”示意图
上面的代码一定看的眼花缭乱吧?呵呵,我一开始看的时候,也被弄得很糊涂。请看下面的示意图,就会对这个环形缓冲有个好的理解了。
5、“溢写”过程
bufend = bufmark; // 在startSpill方法中 sortAndSpill(); bufstart = bufend;
即:
溢写完毕后,原来的bufmark变成了bufstart
6、缓存为什么要设计成环形的?有什么好处?
答:使输入输出并行工作,即“写缓冲”可以和“溢写”并行。“溢写”工作由单独的线程来做。
解读“溢写”代码: bufend = bufmark; // 在startSpill方法中 sortAndSpill(); bufstart = bufend; 1)溢写前: bufend = bufmark; 则溢写的范围是:从bufstart到bufend。 2)在溢写的过程中,bufmark还是有可能增长的! 3)溢写完毕,bufstart = bufend;
*** THE END***
相关推荐
3. **Collect收集阶段**:map()函数处理后的结果被收集到内存缓冲区,按Partitioner进行分区,然后存储到环形内存缓冲区中。 4. **Spill阶段**: - **步骤1**:当内存缓冲区满时,数据将被排序(先分区,再按照key...
2. io.sort.mb属性,int类型,Map端使用该属性设置对Map输出进行排序时使用的环形内存缓冲区的大小,以M字节为单位,默认是100M。如果允许,应该增加它的值来减少磁盘溢写的次数以提高性能。 3. io.sort.record....
5. **内存缓冲与溢写**:数据写入环形内存缓冲区,当达到溢写条件(如默认的80%满)时,数据会被排序并写入磁盘的临时文件。如果配置了Combiner,会在此步骤中对相同key的value进行局部聚合,减少磁盘I/O。 6. **...
网卡通常使用环形缓冲区(DMA环形缓冲区)来管理数据包的接收和发送,这种缓冲区位于处理器共享的内存中。每当接收到一个新的数据包,它就会被放置在环形缓冲区的下一个可用位置,并触发一个中断。随后,驱动程序将...
- `mapreduce.task.io.sort.mb` 设置Shuffle阶段的环形缓冲区大小,过大可能导致内存浪费,过小可能频繁溢出。 - `mapreduce.map.sort.spill.percent` 定义缓冲区溢出的阈值,决定何时将数据写入磁盘。 4. **容错...
- 环形缓冲区:为了更高效地管理内存,MapReduce 使用环形缓冲区,它允许可重用内存空间,避免频繁分配和释放。 2. Reduce 阶段扩展: - Fetch 过程:Reduce 任务通过多个线程并行抓取 Map 输出,线程数量默认为 ...
是一种用于表示一个固定尺寸、头尾相连的缓冲区的数据结构,适合缓存数据流。它的学术上的研讨不在本文的展开范围内,简而言之,不但是通信开发(Socket,TCP/IP,RPC开发)中,在内核的IPC中,在视频音频播放中,在...
在Spill阶段,即“溢写”,当环形缓冲区满后,MapReduce会将数据写到本地磁盘上,生成一个临时文件。在这个阶段,MapTask会将数据写入本地磁盘之前,先要对数据进行一次本地排序,并在必要时对数据进行合并、压缩等...
2. **中间数据处理**:map任务产生的输出会暂时存放在一个环形内存缓冲区中。当该缓冲区快满时(通常达到80%),会触发一次溢出操作,即将内存中的数据写入本地磁盘上的临时文件。 3. **分区与排序**:在写入磁盘...
- **Buffer (`struct tcp_ring_buffer *rcvbuf`)**:接收数据的环形缓冲区。 - **Send Variables (`struct tcp_send_vars`)** - **Buffer (`struct tcp_send_buffer *sndbuf`)**:发送数据的缓冲区。 #### Send ...
- **环形缓冲区管理**:环形缓冲区采用三级索引机制管理数据,包括分区号、键值对的起始位置以及实际的数据存储位置。 - **排序过程**:在MapReduce的整个过程中,经历了多次排序操作,包括: - Map端对分区号的...
它使用环形缓冲区(Ring Buffer)的数据结构,可以高效地追加写入和顺序读取,同时支持多生产者和多消费者模型。 2. **Chronicle Map**:这是一种分布式内存映射的键值存储,提供高效的并发访问和内存管理。它不是...
环形缓冲区、缓冲暂停、排序和溢写等都是Map阶段的关键操作和优化手段。推测式执行用于减少延迟,并提高MapReduce作业的吞吐量。此外,跳过环记录、JVM重用等优化技术也被提及,旨在提高资源利用率和处理效率。任务...
环形缓冲区是一种内存管理策略,它使用一个固定大小的缓冲区并循环使用其空间。在3D渲染中,环形缓冲区常用于管理帧间数据,例如命令缓冲区,因为它允许GPU和CPU并行操作,而不会导致数据冲突。当GPU处理旧数据时,...
创建环形缓冲区对象 `MapOutputCollector`对象是`NewOutputCollector`对象的内部类,它负责收集键值对并写入到缓存区中。`MapOutputCollector`对象的创建过程是通过反射机制来实例化的。首先,它会创建一个`Context...
同时,设备驱动还需要管理DMA缓冲区,确保数据的完整性和一致性。 `dma_alloc_coherent()`函数用于分配一致性内存,这种内存对DMA操作来说是必要的,因为它消除了CPU缓存和设备之间可能出现的数据不一致问题。而`...
MapTask首先将处理结果暂存到一个环形缓冲区,当缓冲区利用率到达一定阈值时,会执行快速排序并将排序后的数据溢写到磁盘上。最后,所有磁盘上的文件会进行一次归并排序,生成多个有序的小文件。 2. ReduceTask排序...
大数据-互联网大厂面试真题附含答案高频面试题.pdf 本资源旨在帮助大数据领域的专业人士准备面试,涵盖了 ... + 增大环形缓冲区溢写的比例 + 减少对溢写文件的 merge 次数 + 采用 Combiner 提前合并,减少 I/O
在中断模式下,当物理网卡接收到新的数据包并将其放入网卡的环形缓冲区时,硬件会触发中断请求。这个中断请求会通知操作系统有事件需要处理。 在Linux系统中,中断服务程序的注册通常在设备初始化或打开时进行,...
- **减少溢写次数**:可以通过调整`mapreduce.task.io.sort.mb`(默认100M)和`mapreduce.map.sort.spill.percent`(默认80%)来增加环形缓冲区大小和溢出阈值,从而减少溢写次数。 - **异常重试**:通过设置`...