先说一下mr的二次排序需求:
假如文件有两列分别为name、score,需求是先按照name排序,name相同按照score排序
数据如下:
jx 20
gj 30
jx 10
gj 15
输出结果要求:
gj 15
gj 30
jx 10
jx 20
我们常见的实现思路是:
1. 自定义类,重写compare()比较逻辑(先比较name,name相同比较score),这样可以保证无论map端,还是reduce端的排序规则是我们需求的
当然,就这道题来说可以使用组合key,name_score吗?其实不行,主要因为score会按照字典排序
2. 我们按照key中的name做分区,按照需求只能有一个reduce,否则name不会全局有序。
然后是不是就ok了呢,如果就结果来说是ok的。但是内部隐藏种种问题。
现在需求换了,我要输出:
gj 15,30
jx 10,20
那么按照之前的逻辑,立马崩盘了。达不到此需求的效果。
我觉得二次排序重点考察之一就是隐藏的grouping。
grouping是做什么的呢,她是reduce端的分组,她是决定reduce方法会被框架调用几次关键,之前的需求之所以成功是因为grouping的compare()默认实现是迭代的前后对象==,
也就是比较对象的内存地址,对象不同所以就返回false,也就是不同组,这时reduce方法会被再次调用,而不是内部values的迭代器了。
由于reduce端的归并排序规则(之前我们已经定义好了),直接输出就ok了,相当于每行数据就调用一次reduce方法。
但如果是第二次需求,没有实现grouping,无法实现相同名字的分数都好分隔。
实现方式就是实现grouping,重写compare方法,逻辑是如果名字相同就返回true。
这样到reduce端,相同name就是reduce同组,一次reduce方法,迭代values内容就可以实现value之间的逗号分隔。
那为什么我们刚学mr是的wordcount不用实现grouping呢?
主要是wordcount的key是string,到了reduce端相同的string内容是有字符串常量池的,所以 == 会相同,这样相同的word单词会同组,会在同一个values迭代器累加。
如果手贱,把string 封装成对象,并且不实现grouping,那得到的结果就不是我们想要的
会变成:
a 1
a 1
b 1
b 1
...
思考问题:
1. 一般的二次排序key如何定义?
2. grouping 是不是一定要实现,不实现可以吗?
3. 二次排序的本质是什么?
4. 如果以下输出
gj 15,30
jx 10,20
1). 可不可以不设置grouping
2). key可不可以设置为name
- 一般自定义对象,但是如果比较的东东都是string,并且需求是字典序,那就可以用string的组合key。
- 如何要实现二次排序,grouping是要实现的,但是像第一种需求没重写grouping结果恰巧也对。
- 笔者认为本质:考察对mr整个数据流向的理解,还有关键的reduce分组理解是否深入
- 其实根据需求有时候不实现也可以, 可以定义一个全局中间变量,判断当前name与上一个name是否一样,一样就拼接value,不一样就write,不过中间要多定义几个全局临时变量,用于数据交换,不推荐这么使用。可以把可以key定义为name不过这样reduce压力较大,value(score)的排序也会在reduce内存中进行,数据量大也会有问题,不推荐。