spark有两种类型的共享变量:累加器和广播变量。累加器用来对信息进行聚合,广播变量用来高效分发较大的对象。
1、累加器使用
应用场景:
我们想在并发读取日志文件的同时统计出总共有多少空行。这需要一个全局变量,spark里提供了一个累加器可以实现这种效果。
spark 2.X的代码如下:
SparkConf conf = new SparkConf();
conf.setAppName("RealNameDS");
conf.setMaster("local");
JavaSparkContext jsc = new JavaSparkContext(conf);
JavaRDD<String> lineRDD = jsc.textFile(input);
LongAccumulator blankLines = jsc.sc().longAccumulator("blankLines");
lineRDD.flatMap(new FlatMapFunction<String, String>() {
@Override
public Iterator<String> call(String line) throws Exception {
if("".equals(line.trim())) {
blankLines.add(1);
}
return Arrays.asList(line.split(" ")).iterator();
}
});
//第一个行动操作
wordRDD.count();
System.out.println("blank lines: " + blankLines.value());
//第二个行动操作
wordRDD.saveAsTextFile("output.txt");
System.out.println("blank lines: " + blankLines.value());
2、累加器的坑与容错性
Accumulator的值只有在driver里才能访问,在工作节点上的task不能访问累加器的值。从task的角度来看,累计器是一个只写变量。查看Accumulator的值也只能在driver里。
RDD的转化操作是惰性的,所以作为计算副产品的累加器只有在行动操作之后才能有真实值。
注意:每经过一个行动操作,都会触发RDD重新计算,所以累加器的值只有在第一个行动操作之后是准确的。上面例子中两次打印的blankLines.value()值不一样。如果需要使用多次则使用cache或persist操作切断依赖。
由于spark集群会自动重新执行失败或较慢的任务来应对有错误的或者比较慢的机器,所以在RDD转化操作中使用的累加器是不能保证绝对可靠的。转化操作中的累加器最好只在调试时使用。如果想要一个无论在失败还是重复计算时都绝对可靠的累加器,必须把它放在foreach()这样的行动操作中。
3、自定义累加器
自定义的累加器要保证里面的操作同时满足交换律和结合律,例如要找累加的值中的最大值。