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的实现决定的,后续会提到。

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