在Flink DataSet上应用多个JOIN的分区策略

时间:2018-03-02 18:07:05

标签: java join apache-flink

我正在使用Flink 1.4.0

假设我有POJO如下:

public class Rating {
    public String name;
    public String labelA;
    public String labelB;
    public String labelC;
    ...        
}

JOIN函数:

public class SetLabelA implements JoinFunction<Tuple2<String, Rating>, Tuple2<String, String>, Tuple2<String, Rating>> {

    @Override
        public Tuple2<String, Rating> join(Tuple2<String, Rating> rating, Tuple2<String, String> labelA) {
        rating.f1.setLabelA(labelA)
        return rating;
    }
}

并假设我想应用JOIN操作来设置DataSet<Tuple2<String, Rating>>中每个字段的值,我可以这样做:

DataSet<Tuple2<String, Rating>> ratings = // [...]
DataSet<Tuple2<String, Double>> aLabels = // [...]
DataSet<Tuple2<String, Double>> bLabels = // [...]
DataSet<Tuple2<String, Double>> cLabels = // [...]
...
DataSet<Tuple2<String, Rating>>
            newRatings =
            ratings.leftOuterJoin(aLabels, JoinOperatorBase.JoinHint.REPARTITION_SORT_MERGE)

                   // key of the first input
                   .where("f0")

                   // key of the second input
                   .equalTo("f0")

                   // applying the JoinFunction on joining pairs
                   .with(new SetLabelA());

不幸的是,这是必要的,因为评分和所有xLabels都非常大DataSets,我不得不调查每个xlabels以找到我需要的字段值,而同时,并非每个xlabels都存在所有评级密钥。

这实际上意味着我必须每leftOuterJoin执行xlabel,为此我还需要创建相应的JoinFunction实现,该实现使用{{1}中的正确设置器} Rating

有没有更有效的解决方法,任何人都可以想到?

就分区策略而言,我确保对POJO进行排序:

DataSet<Tuple2<String, Rating>> ratings

通过将并行度设置为1,我可以确保整个数据集将被订购。然后我使用DataSet<Tuple2<String, Rating>> sorted_ratings = ratings.sortPartition(0, Order.ASCENDING).setParallelism(1);

.partitionByRange

其中DataSet<Tuple2<String, Rating>> partitioned_ratings = sorted_ratings.partitionByRange(0).setParallelism(N); 是我在VM上拥有的核心数。我在这里遇到的另一个问题是,设置为1的第一个N是否在执行管道的其余部分方面具有限制性,即后续.setParallelism是否可以改变{{1}的方式处理?

最后,我完成了所有这些操作,以便在.setParallelism(N)DataSet partitioned_ratings结合后,JOIN操作将使用xlabels完成。根据{{​​1}}的{​​{1}}文档:

  

REPARTITION_SORT_MERGE:系统对每个输入进行分区(shuffle)(除非输入已经被分区)并对每个输入进行排序(除非它已经排序)。输入通过已排序输入的流合并来连接。如果已经对一个或两个输入进行了排序,则此策略很有用。

所以在我的情况下,DataSet已排序(我认为),而JoinOperatorBase.JoinHint.REPARTITION_SORT_MERGE Flink中的每一个都没有,因此这是最有效的策略。这有什么不对吗?任何替代方法?

1 个答案:

答案 0 :(得分:0)

到目前为止,我还未能完成这一策略。似乎依赖JOINs太麻烦了,因为它们是昂贵的操作,除非它们确实是必要的,否则应该避免它们。

例如,如果JOINs的大小非常大,则应使用Datasets。如果不是,一个方便的替代方案是使用BroadCastVariables,其中两个Datasets(最小的)之一,无论用于何种目的,都会在工作人员之间进行广播。下面会显示一个示例(为方便起见,从此link复制)

DataSet<Point> points = env.readCsv(...);

DataSet<Centroid> centroids = ... ; // some computation

points.map(new RichMapFunction<Point, Integer>() {

    private List<Centroid> centroids;

    @Override
    public void open(Configuration parameters) {
        this.centroids = getRuntimeContext().getBroadcastVariable("centroids");
    }

    @Override
    public Integer map(Point p) {
        return selectCentroid(centroids, p);
    }

}).withBroadcastSet("centroids", centroids);

此外,由于填充POJO的字段意味着将重复利用非常相似的代码,因此必须使用jlens来避免代码重复并编写更简洁易懂的解决方案。