承接上文MapReduce之Map阶段。
我们需要将map后的数据往外写。
收集数据
我们写出的数据是("I", 1)
。
我们需要往kvbuffer中写key和value。
写key的时候我们既要写I
,又要写它的位置,不然怎么能找到它呢?
写的时候,如果剩余内存不够用了,就会发生溢写:
写I
之前,我们已经有1这个位置了:
写了I
之后就有了它的ASCII值了:
接着写1
这个值,因为我们的1
是Long,占8个字节,所以会占用kvbuffer的8个位置:
此时我们的bufindex增加到了10,它可是每写一次都会往上涨的。
接下来就要存元数据了:
注意到那几个常数:
这里就存了四个数,一个是分区,因为这里是I
,按照自定义分区,分区为1。然后还有key开始的位置,value开始的位置,value的长度(此处为8)。
还记得我们刚开始的kvindex是21264396,经过计算现在的kvindex是21264392:
这就说明它往前跳了4格,因为接下来kvindex同样要+0,+1,+2,+3存分区,keyStart,valueStart,valueLength。
我们再看写出like
的情况:
同样,kvmeta也要put一波,kvindex再次减4:
如果bufferRemaining
用完了,就会发生溢写。(不管是kvbuffer中存数据,还是kvmeta中存元数据,bufferRemaining
都会不断减少)。
另外一种溢写的条件是所有的数据都读完了。
我们紧跟最后一个词hadoop
的脚步,看发生了什么:
排序和溢写
我们先记录下最后的kvbuffer和kvmeta相关参数:
一路跟下去:
我们看到map阶段结束了。现在进入output.close(mapperContext)
。
我们看到它首先进行排序:
首先注意一点,就是在排序前后,kvbuffer并没有变:
这是我将其复制出来进行观察的结果:
0 = 1
1 = 73
2 = 0
3 = 0
4 = 0
5 = 0
6 = 0
7 = 0
8 = 0
9 = 1
10 = 4
11 = 108
12 = 105
13 = 107
14 = 101
15 = 0
16 = 0
17 = 0
18 = 0
19 = 0
20 = 0
21 = 0
22 = 1
23 = 4
24 = 106
25 = 97
26 = 118
27 = 97
28 = 0
29 = 0
30 = 0
31 = 0
32 = 0
33 = 0
34 = 0
35 = 1
36 = 1
37 = 73
38 = 0
39 = 0
40 = 0
41 = 0
42 = 0
43 = 0
44 = 0
45 = 1
46 = 4
47 = 108
48 = 105
49 = 107
50 = 101
51 = 0
52 = 0
53 = 0
54 = 0
55 = 0
56 = 0
57 = 0
58 = 1
59 = 5
60 = 115
61 = 99
62 = 97
63 = 108
64 = 97
65 = 0
66 = 0
67 = 0
68 = 0
69 = 0
70 = 0
71 = 0
72 = 1
73 = 1
74 = 73
75 = 0
76 = 0
77 = 0
78 = 0
79 = 0
80 = 0
81 = 0
82 = 1
83 = 4
84 = 108
85 = 105
86 = 107
87 = 101
88 = 0
89 = 0
90 = 0
91 = 0
92 = 0
93 = 0
94 = 0
95 = 1
96 = 6
97 = 112
98 = 121
99 = 116
100 = 104
101 = 111
102 = 110
103 = 0
104 = 0
105 = 0
106 = 0
107 = 0
108 = 0
109 = 0
110 = 1
111 = 1
112 = 73
113 = 0
114 = 0
115 = 0
116 = 0
117 = 0
118 = 0
119 = 0
120 = 1
121 = 4
122 = 104
123 = 97
124 = 116
125 = 101
126 = 0
127 = 0
128 = 0
129 = 0
130 = 0
131 = 0
132 = 0
133 = 1
134 = 6
135 = 104
136 = 97
137 = 100
138 = 111
139 = 111
140 = 112
141 = 0
142 = 0
143 = 0
144 = 0
145 = 0
146 = 0
147 = 0
148 = 1
149 = 0
其次,它排序排的是元数据,就是kvmeta的排序,先按分区排,分区一样的按照key排。
溢写的时候是按照分区一个一个溢写的,每个分区都是反向溢写,也就是说,我们先循环0号分区,然后0号分区最后一个kv是("hadoop", 1)
,那么我们就会先溢写这对kv:
数据溢写完之后,就会有一个spill0.out的文件:
当然现在索引数据还在内存,没有到达1MB:
合并
我们可能会得到许多spill.out和spill.index文件,我们需要将其合并成一个大文件,这可以避免同时读取大量小文件带来的开销。
在合并之前,我们释放了kvbuffer的内存:
因为我们的溢写文件只有一个,所以只需改名就行了。
在sort阶段结束后,我们有了一个数据文件和一个索引文件:
此时shuffle阶段结束。