Data Algorithm》读书笔记五之 — 反转排序

1. 什么是反转排序

Hadoop 中的反转排序并不是反过来排序。【即使是反过来排序,也是一种排序,就不用搞一个反转排序 的名字出来了】那么MapReduce 中的反转排序到底是什么意思呢?
从事java 开发的相信都知道IOC,即控制反转(Inversion of Control)。
以下是百度百科中对IOC的介绍,我这里不再赘述了。

IOC 是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI)…

同样,如果想要在 MapReduce job中,让开发者自定义排序规则,即反转排序。【我理解的定义】

2. 反转排序能干啥?

反转排序是MapReduce job中的一种设计模式!
反转排序是MapReduce job中的一种设计模式!
同java 工厂模式策略模式等等一样,它们只不过都是一种抽象的定义,具体的实现则是要通过代码展示出来。反转排序主要提供以下功能:

  • 用于控制 MapReduce 框架中归约器值的顺序
  • 使用 OI 模式的唯一目的是: 适当地确定提供给归约器的数据的顺序
  • 定制一个分区器,它只关注组合键(K1,K2)左边的部分(K1,即自然归约器键)。定制分区器只根据左键(K1)的散列进行分区

3. 简单示例

  • 在本博客中,通过计算一个文档中单词的相对频度来展示 OI模式(只是用这个例子去展示OI模式,但是不能说这个例子就是OI模式) 。
  • 例如,针对文本:W1,W2,W5,W4,W5,W6。如果定义一个单词的邻域为这个单词的前后两个单词,那么这6个单词的邻域表示如下:
<W1,W2>
<W1,W5>

<W2,W1>
<W2,W5>
<W2,W4>

<W5,W1>
<W5,W2>
<W5,W4>
<W5,W5>

<W4,W2>
<W4,W5>
<W4,W5>
<W4,W6>

<W5,W5>
<W5,W4>
<W5,W6>

<W6,W4>
<W6,W5>

因为计算相对频度需要得到边缘计数(所谓边缘计数就是频度总数)。在得到所有计数之前,将无法计算边缘计数。因此,需要让边缘计数在联合计数之前到达归约器(这一点可以通过使用二次排序实现)。
计算一个给定文档集的相对频度。需要生成两个数据序列。

  • 第一个序列是单词的总邻域计数(一个单词的总共现次数),用组合键(w,*)表示,其中w表示单词;
  • 第二个序列是这个单词与其他特定单词的共现次数,用组合键(w,w2)表示。
KEY		VALUE
(W,*)	A1,A2,A3
(W,W1)	B1,
...
(W,Wn)	N1,N2,N3,N4...

Mapper类中,不仅向Reducer发送(W,W1),也会发(W,*)。但是需要保证的是:(W,*)(W,W1)要先到达reducer。然后在 reducer 中进行一个数量的统计,这样就可以保证每个(W,*) 会被先统计,然后就知道有多少个数量了。
针对上述的实例:得到的如下的键值对:

(<W1,W2> , 1)
(<W1,W5> , 1)

(<W2,W1> , 1)
(<W2,W5> , 1)
(<W2,W4> , 1)

(<W4,W2> , 1)
(<W4,W5> , 1)
(<W4,W5> , 1)
(<W4,W6> , 1)

(<W5,W1> , 1)
(<W5,W2> , 1)
(<W5,W4> , <1,1>)
(<W5,W5> , <1,1>)
(<W5,W6> , 1)

(<W6,W4> , 1)
(<W6,W5> , 1)

例如,key.leftStr = W5 的都会被分到同一个分区中。在发送上述的键值对之外,还会再发送<W5,*>,<1,1,1,1,1,1,1>

4. 如何实现反转排序?

反转排序的实现主要是通过 二次排序自定义分区方法等实现。

5.实现代码

5.1 GroupPair
package data_algorithm.chapter_5;

import org.apache.hadoop.io.WritableComparable;
import org.apache.hadoop.io.WritableComparator;

public class GroupPair extends WritableComparator {
    public GroupPair(){
        super(PairOfWord.class,true);
    }



    @Override

    public int compare(WritableComparable a, WritableComparable b) {
        System.out.println("");
        PairOfWord p1 = (PairOfWord) a;
        PairOfWord p2 = (PairOfWord) b;
        int status = p1.getLeftStr().compareTo(p2.getLeftStr());
        if (status == 0) {
            status = p1.getRightStr().compareTo(p2.getRightStr());
        }
        return status;
    }
}
5.2 PairOfWord
package data_algorithm.chapter_5;

import org.apache.hadoop.io.Writable;
import org.apache.hadoop.io.WritableComparable;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

/**
 * 01.这个PairOfWord 是一个<key-value>对中的 key。 这个key由两个部分组成:分别是leftStr 和 rightStr
 */
public class PairOfWord implements Writable,WritableComparable<PairOfWord>{

    private String leftStr;
    private String rightStr;

    public PairOfWord() {
    }

    public PairOfWord(String leftStr, String rightStr) {
        this.leftStr =leftStr;
        this.rightStr = rightStr;
    }

    public String getLeftStr() {
        return leftStr;
    }

    public void setLeftStr(String leftStr) {
        this.leftStr = leftStr;
    }

    public String getRightStr() {
        return rightStr;
    }

    public void setRightStr(String rightStr) {
        this.rightStr = rightStr;
    }
    @Override
    public void write(DataOutput out) throws IOException {
        out.writeUTF(this.leftStr);
        out.writeUTF(this.rightStr);
    }

    @Override
    public void readFields(DataInput in) throws IOException {
        this.leftStr = in.readUTF();
        this.rightStr = in.readUTF();
    }

    @Override
    public int compareTo(PairOfWord o) {
        //以PairOfWord 的leftStr作为比较对象
        int compareValue = this.leftStr.compareTo(o.leftStr);
        if (compareValue == 0) {
            compareValue = this.rightStr.compareTo(o.getRightStr());
        }
        return compareValue;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        PairOfWord that = (PairOfWord) o;

        if (leftStr != null ? !leftStr.equals(that.leftStr) : that.leftStr != null) return false;
        return rightStr != null ? rightStr.equals(that.rightStr) : that.rightStr == null;
    }

    @Override
    public int hashCode() {
        int result = leftStr != null ? leftStr.hashCode() : 0;
        //result = 31 * result + (rightStr != null ? rightStr.hashCode() : 0);
        result = 31 * result ;
        return result;
    }

    @Override
    public String toString() {
        return "<" + leftStr + ","+ rightStr  + '>';
    }
}
5.3 其它代码

其它的代码诸如MapperReducer 等我在这里就不介绍了,详情可以通过我的 github 查看。

6. 实现结果

6.1 输入文件
[root@server4 hadoop]# hdfs dfs -cat /input/orderInversion.txt
W1,W2,W5,W4,W5,W6
6.2 输出文件
[root@server4 hadoop]# hdfs dfs -cat /output/orderInversion/part*
<W5,W1>	0.14285714285714285
<W5,W2>	0.14285714285714285
<W5,W4>	0.2857142857142857
<W5,W5>	0.2857142857142857
<W5,W6>	0.14285714285714285
<W1,W2>	0.5
<W1,W5>	0.5
<W6,W4>	0.25
<W6,W5>	0.25
<W2,W1>	0.3333333333333333
<W2,W4>	0.3333333333333333
<W2,W5>	0.3333333333333333
<W4,W2>	0.25
<W4,W5>	0.5
<W4,W6>	0.25
6.3 其它测试
java is a great language
java is a programming language
java is green fun language
java is great
programming with java is fun 

现在来看 great 的频率,如下:

(great,is)  
(great,a)
(great,language)
(great,is)
(great,java)

=>
(great,is)       0.2
(great,is)       0.2
(great,a)        0.2
(great,language) 0.2
(great,java)     0.2

=>
(great,is)       0.4
(great,a)        0.2
(great,language) 0.2
(great,java)     0.2

得到的输出结果如下:

《Data Algorithm》读书笔记五之 — 反转排序_# 《Data Algorithms》