如何加速Spark SQL单元测试?

时间:2015-11-29 14:57:16

标签: unit-testing testing apache-spark apache-spark-sql

我正在评估Spark SQL以实现一个简单的报告模块(对已经存储在HDFS上的Avro数据进行的简单聚合很少)。我毫不怀疑Spark SQL可以很好地适应我的功能和非功能需求。

但是,除了生产要求之外,我还要确保模块可以测试。我们遵循BDD方法,采用非常集中的方案,这意味着该模块将需要对一些非常简单的数据(1..10条记录)运行数十/数百个SQL查询。

为了大致了解我在本地模式下可以从Spark SQL中获得的性能,我已经快速制作了一些测试原型:

  1. select count(*) from myTable
  2. select key, count(*) from myTable group by key
  3. 第一次测试平均需要100ms,但第二次需要500ms。这种性能是不可接受的,因为它会使测试套件太慢。

    为了比较,我可以使用Crunch及其MemPipeline(在本地模式下使用MRPipeline为1500ms)以及在嵌入模式下使用Hive进行1500ms运行相同的测试。因此,Spark SQL在本地模式下比MR快一点,但仍然可以减慢构建良好测试套件的速度。

    是否可以在本地模式下加速Spark SQL?

    是否有更好/更快的方法来测试Spark SQL模块?

    (我还没有分析执行情况,但由于RDD上的groupBy().countByKey()平均需要40毫秒,我希望发现罪魁祸首是查询优化器)

    我的快速&脏测试代码如下:

      SparkConf sparkConf = new SparkConf()
                    .setMaster("local[4]")
                    .setAppName("poc-sparksql");
    
      try (JavaSparkContext ctx = new JavaSparkContext(sparkConf)) {
            SQLContext sqlCtx = new SQLContext(ctx);
    
            for (int i = 0; i < ITERATIONS; i++) {
                Stopwatch testCaseSw = new Stopwatch().start();
    
                DataFrame df = sqlCtx.load("/tmp/test.avro", "com.databricks.spark.avro");
                df.registerTempTable("myTable");
                DataFrame result = sqlCtx.sql("select count(*) from myTable");
    
                System.out.println("Results: " + result.collectAsList());
                System.out.println("Elapsed: " + testCaseSw.elapsedMillis());
            }
    
            for (int i = 0; i < ITERATIONS; i++) {
                Stopwatch testCaseSw = new Stopwatch().start();
    
                DataFrame df = sqlCtx.load("/tmp/test.avro", "com.databricks.spark.avro");
                df.registerTempTable("myTable");
                DataFrame result = sqlCtx.sql("select a, count(*) from myTable group by a ");
    
                System.out.println("Results: " + result.collectAsList());
                System.out.println("Elapsed: " + testCaseSw.elapsedMillis());
            }
     }
    

3 个答案:

答案 0 :(得分:1)

当数据量非常小时,启动太多任务不是一个好选择。
在第二个选项group by中,将创建另一个stage 200 tasks,因为您未设置shuffle partitions属性,默认情况下为200,其中大多数将为空。

在单个测试中可能不会有什么不同,但是当您使用随机操作进行数千个测试时,可能会产生重大影响。

在spark conf中将"spark.sql.shuffle.partitions"设置为x (where x is local[x])。

实际上,您不需要4 executors处理少于10条记录,因此更好地将执行者的数量减少到1,并将shuffle.paritions设置为1

答案 1 :(得分:0)

如果您正在寻找ms级别的优化,那么有各种各样的指针。

  1. 读取您的数据一次,并多次缓存,只查看SQL查询。循环内部加载意味着“在everyIteartion中生成新任务”
  2.  DataFrame df = sqlCtx.load("/tmp/test.avro","com.databricks.spark.avro");
     df.registerTempTable("myTable");  
     df.cache()
    
     for (int i = 0; i < ITERATIONS; i++) {
           Stopwatch testCaseSw = new Stopwatch().start();
           DataFrame result = sqlCtx.sql("select count(*) from myTable");
           // Dont do printLn inside the loop , save the output in some hashMap and print it later once the loop is complete
           System.out.println("Results: " + result.collectAsList());
           System.out.println("Elapsed: " + testCaseSw.elapsedMillis());
    }
    
    1. 将循环外的System.out.println抽出,耗费一段时间。
    2. 请看一下: http://bytepadding.com/big-data/spark/understanding-spark-through-map-reduce/

答案 2 :(得分:0)

我使用Holden Karau开发的spark-testing-base库在Spark中进行单元测试。

在相对的README.md中,您可以找到有关调整资源以分配给单元测试的更多信息。