去重过程同样很简单,不过需要些技巧。
正如你想的一样,在入手之前我自己也分析,怎样根据url & md5去重呢?去重又怎样实现呢?
*其中我当时想法跟job1(见下面)做法是相近的:url+time;
*而md5去重时我又觉得使用md5+time亦未尝不可,因为当时我没有考虑到score因素,但可以断定的是不能使用md5作为distinct,因为这就是题目要求;
*至于怎样去删除索引 ,当时也没有考虑:)
让我们先分析一个删除例子:
A多个urls中,如果存在相同的urls,只取最新的一个;
B如果存在相同md5的urls,只保留评分最高或url最短的一个。
data model:
url | fetch time | content | score |
a.cn | 2011-1-1 | a | 2 |
a.cn | 2011-7-1 | b | 4 |
a.cn | 2011-5-1 | a | 1 |
b.cn | 2011-4-1 | b | 2 |
要么,按照A要求 ,第一轮后输出結果如下 :
a.cn | 2011-7-1 | b | 4 |
b.cn | 2011-4-1 | b | 2 |
再根据B要求,发现这两urls含有相同的content,所以再进行score筛选,結果为:
a.cn | 2011-7-1 | b | 4 |
如果b.cn的评分比a.cn高时,则为另一結果。可见,从实际 上出发,这也是符合需求的。
因为相同的有相同的urls只取最后 一个;当有相同的content时只取score最高的一个,这样导致二种結果:
1.要么相同的urls中有一个最新的留下来了;或者
2.内容相同的url只保留score高的一个。
即取舍原则是:最新的优先;分数高的优先。NOTE:上面先url再md5这个过程是可逆的 。(可以分析一下上面的反过程,結果是一样的)
至于为什么先进行url再到md5,其实已经不太重要了?
*****************************
注意以上过程未严格的考虑url与content的对应关系,极端情况下,url与content 是m:n的关系,如
一个url可以对应多个content,比如页面中存在时间,不同的访问时间生成的内容不一样;
同样的content,如404页面,只要是访问了不存在的url都会进行这个页面。
******************************
好了,现在开始进行分析,其实上面过程也大概反应了这个dedup的过程:
1.url-distinct job
input :利用inputFormat改写了输入格式,在next()时对<url,Indexdoc>进行了数据shipping,其中indexdoc是对索引 中的doc的简单encapsulation,包括hash,score,tstamp etc.
*其中next方法遍历了所有的doc;
*同时拆分原则也是一份index一个split,这样保证在递归输入<key,value>到mapper之前 是可以让next()中的reader完整读取index,因为一个split生成一个对应 的recordReader;
*同时让url作为key也可以保证了处理需求,即上述A要求
mapper :use default
reducer :对url相同的进行distinct,如果是最后 个则标记为keep=true,表明是将要以md5与其它比较取舍。
output :<MD5Hash,indexdoc>,在reduce中对上述的key进行了转换,方便后续job处理。
note:这里使用了默认的partition,所以相同 的url去到想再的reducer,保证了不至于url混跑。
2.md5-distinct job
input:上述输出
mapper:default
partition:对md5进行hash。注意已经改写了hashCode(),只返回高四位的hash
reducer:将job1中标记为false的直接输出(待删除索引 );只保留true的一个(如果存在),然后与其中md5相同的url进行比对,得出score top1或min(url lengh)的一个(不输出)
output:注意些又将job1中的<md5,indexdoc>退回它的输入格式,即<url,indexdoc>
3.delete-index job
input:如上
mapper:对上述的输出进行转换:<index-path,docid>
reducer:根据 indexpath和docid进行删除索引。(其实这个reader不可以提取出来复用,因为这是根据 不同的indexpath进行发分的)
output:由于索引已经在red阶段已经处理了,没有输出必要,所以改写了outputformat.
*索引删除时是按照docid进行了,一方面增高性能;其次是如果利用url,因为可能相同的有多个(如上述)这述造成删除错误。
其中具体的删除流程如下图:
环境假定:
有三份索引 (所以有三个mappers)
指定二个reducers(固每次map之后都有二个r,数据均匀的理论值)
默认使用SequenceFileInputFormat(splitable)
分析 :
在job2-3之间的mappers是不确定 的,根据 当时的运行情况进行splits;
在每次map完后又恢复至二个r;
在job3,我们有理由 相信其中的一个r被分发的数据大于另一个,这是因为分发时使用了indexpath-hash来实现的。固图中浓黑色的一个数据是多的,另一个是小的;
所以由数据多的r将负责index dedup的任务就多了一个;
总之,一个red可以对应 多个index dedup(由于r处理时是按key按sequential order input的,所以不会出现 一份索引 被多个r处理的局面,初初看到其中的reader在每次r时构成一个感觉耗费很大,其实不然);但反之不然。