apache beam 入門之數據聚合

聚合元素(多個元素合併成1個)操作(類似於reduce操作)

如果要將數據集裏的所有元素聚合成1個元素,在beam裏稱爲combine操作。
假設現在我們有1個PCollection數據集 pInt
則我們以計算整數求和的方式,展示3種聚合方式:

用beam提供的sdk

PCollection<Integer> pSum = pInt.apply(Sum.integersGlobally());

用Combine.globally(SerializableFunction類)

PCollection<Integer> pSum = pInt.apply(Combine.globally(new IntergerSum());

IntegerSum定義如下:

    //實現SerializableFunction<A,B>接口, A是管道里元素的迭代器類型, B是輸出結果類型
    public static class IntergerSum implements SerializableFunction<Iterable<Integer>, Integer> {
        @Override
        public Integer apply(Iterable<Integer> input) {
            Integer sum = new Integer(0);
            for (Integer item : input) {
                sum += item;
            }
            return sum;
        }
    }

自定義Combine.CombineFn

這個方法較複雜,要實現4個接口,但是自由度也會高很多
可以用中間累加器做特殊的聚合操作,並最後再切換回需要的輸出

PCollection<Integer> pSum = pInt.apply(Combine.globally(new IntSumFn());
/**
 * 繼承自Combine.CombineFn<A,B,C>
 * A輸入管道的元素, B中間累加器的類型,  C輸出結果類型
 * 步驟:創建累加器、各機器管道元素合到累加器中、各管道累加器合併、處理最終結果
 */
class IntSumFn extends Combine.CombineFn<Integer, Integer, Integer> {
    // 中間累加器可以自己定義

    // 中間累加器初始化
    @Override
    public Integer createAccumulator(){ return 0;}

    //單管道中的累加器操作
    @Override
    public Integer addInput(Integer accum, Integer input){
        accum += input;
        return accum;
    }

    //合併多個分佈式機器的累加器方法
    //最終返回1個累加器
    @Override
    public Integer mergeAccumulators(Iterable<Integer> accums){
        Integer merged = createAccumulator();
        for (Integer accum: accums){
            merged += accum;
        }
        return merged;
    }

    //如何將累加器轉化爲你需要的輸出結果
    //這裏可以對最後聚合的輸出結果做特殊處理
    @Override
    public Integer extractOutput(Integer accum){
        return accum;
    }
}

利用CombineFn的中間累加器,可以靈活地實現各種聚合,我們換1個更有用的例子: 我希望將所有字符串元素合併成1個字符串,並轉成自己需要的1個輸出實體OutputEntity
這個需求中,如果要疊加字符串,則肯定不能直接讓String相加,因爲這比較消耗性能,正確的姿勢是用StringBuilder做中間累加器。

class MergeStringToOutputEntity extends Combine.CombineFn<String, StringBuilder, OutputEntity> {

    @Override
    public StringBuilder createAccumulator() {
        return new StringBuilder();
    }

    @Override
    public StringBuilder addInput(StringBuilder mutableAccumulator, String input) {
        return mutableAccumulator.append(input);
    }

    @Override
    public StringBuilder mergeAccumulators(Iterable<StringBuilder> accumulators) {
        StringBuilder mergeAccum = createAccumulator();
        for(StringBuilder stringBuilder : accumulators) {
            mergeAccum.append(stringBuilder);
        }
        return mergeAccum;
    }

    @Override
    public OutputEntity extractOutput(StringBuilder accumulator) {
        return new OutputEntity(accumulator);
    }
}

不過也要注意一點,中間累加器切忌過大, 即使你控制中間累加器最多爲10M,不會超出JVM內存,但也會出現合併過程及其緩慢的情況。這是beam的combine的實現決定的,後續會提到。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章