一、环形缓冲区相关类和属性说明
1、MapTask 或者 MapOutputBuffer:
默认的环形缓冲区类,可以通过job配置文件的参数***mapreduce.job.map.output.collector.class***进行设置。
2、sorter:
默认的排序类,可以通过job配置文件参数**map.sort.class进行设置,此类必须是IndexedSorter类的子类。
3、kvbuffer(环形缓冲区):
是一个字节数组,用于保存mapoutput数据的meta信息(16B),key,value值,默认大小是100M,可以通过参数mapreduce.task.io.sort.mb设置环形缓冲区的大小(单位MB)。
4、kvmeta:
用于存储key、value值的元数据信息。
5、kvindex,bufindex:
分别对应下一个meta数据,和key、value数据将被存放的位置。
6、kvstart,kvend,bufstart,bufend,bufmark:
bufmark用于记录当前插入key的开始位置,记录插入完成后bufmark=bufindex;bufstart是kvbuffer存放key,value数据部分的开始位置,bufend通常等于bufstart,在溢写时等于bufindex的当前值,kvstart和kvend用于管理meta数据,类似bufstart和bufend。
7、bufferreMaining,softLimit:
作为判断是否发生溢写的条件,初始时bufferRemaining=soflimit=kvbuffer.length*溢写占比(默认0.8)。以后每插入一条记录,bufferRemaining - 记录size。
二、环形缓冲区初始化
通过调用MapTask或者MapOutputBuffer的init()方法进行初始化:
设置溢写占比,当使用内存超过一个阈值时就进行溢写,默认为0.8。
设置kvbuffer大小,默认为100M。
通过反射获取sorter对象。
创建kvbuffer,设置equator和其他参数值。
bufstart = bufend = bufindex = equator=0;
bufvoid = kvbuffer.length;
kvstart = kvend = kvindex=bufvoid - matasize(16)。
三、环形缓冲区写入数据
1、当执行mapper子类的map()方法调用context.write(key,value)时,最终会调用MapTask的collect(key,value,partition)方法将key,value和meta数据写入上述的kvbuffer。
2、写入一条记录时,首先判断是否满足溢写条件。
3、如果不满足:
(1)、将key序列化存放在bufindex所指向的位置,如果key值被分割在kvbuffer两端;需要调整key位置,使得key连续。并且设定keystart的值。
(3)、执行后bufindex指向下一个空位置。
(4)、将value序列化存放在bufindex指向的位置,并更新bufindex指向下一个空位置;bufindex一直在增加。
(5)、存放meta数据,meta数据占kvbuffer16个字节;用于存放该记录的keystart,valuestart,partition,valuelength信息。
(6)、将meta数据按valuestart,keystart,partition,valuelength顺序存放。 存放完成后kvindex-16;bufstart,bufend,kvstart和kvend不变。
四、溢写
1、在MapTask的collect(key,value,partition)方法往缓冲区存放数据时,判断到达了溢写条件(bufferRemaining-METASIZE <= 0)并且(已使用缓冲区字节数>= softLimit),则进行溢写。
2、当没有达到一些条件,但是文件数据已经读完了,也会触发溢写。
五、注意点
1、配置参数mapreduce.map.sort.spill.percent:溢写占比(默认0.8)。
2、配置参数mapreduce.task.io.sort.mb:kvbuffer大小(默认100M),单位MB。
3、缓冲区最大0x7ffMB,即2047MB。
4、配置参数map.sort.class:溢写时采用的排序对象,默认是QuickSort。
5、写记录(meta,key,value)时,写入meta是(大地址->小地址),而写入key,value是(小地址->大地址);每次写入一条记录,kvindex-16,bufindex+(key,value实际序列化后所需的空间大小);到达kvbuffer边界时进行%运算。
6、value存在key后面(此处时逻辑上的后面,涉及value部分值越过边界放在kvbuffer另一端)。
7、key需要连续存放(在排序中需要对key进行比较需要连续存放),当key值被分开到kvbuffer俩端时,会对key进行一次重新放置,调整后bufvoid会减小。
8、value值可以不连续存放。
9、meta数据占kvbuffer16个字节;用于存放该记录的keystart,valuestart,partition,valuelength信息;将meta数据按valuestart,keystart,partition,valuelength顺序存放。
10、溢写条件:(bufferRemaining-METASIZE <= 0)并且(已使用缓冲区字节数>= softLimit)
11、比较两个meta大小时,首先比较记录的partition值,其次比较key值。
12、在溢写时,首先根据partition和key的值对meta进行排序,使得缓冲区中的meta按partition和key递增排序。在溢写时会重新调整bufvoid的值。
六、本文是自己个人学习总结,同时也借鉴了其他人的优秀文章。