如何在使用数据帧时下推限制Cassandra的谓词?

时间:2018-03-28 12:28:23

标签: scala cassandra spark-dataframe spark-cassandra-connector

我有大型的Cassandra表。我想从Cassandra只加载50行。 以下代码

val ds = sparkSession.read
      .format("org.apache.spark.sql.cassandra")
      .options(Map("table" -> s"$Aggregates", "keyspace" -> s"$KeySpace"))
      .load()
      .where(col("aggregate_type") === "DAY")
      .where(col("start_time") <= "2018-03-28")
      .limit(50).collect()

以下代码从where方法推送两个谓词,但不限制一个。获取整个数据(100万条记录)是真的吗?如果没有,为什么在没有limit(50)的情况下运行此代码和代码的时间大致相同。

1 个答案:

答案 0 :(得分:4)

与Spark Streaming不同,Spark本身正在尽可能快地预加载尽可能多的数据,以便能够并行地对其进行操作。因此预加载是懒惰的,但是当它被触发时会贪婪。然而,有cassandra-conector特定因素:

  • Automatic predicate pushdown 有效“where”子句。

  • 根据this answer limit(...)未转换为CQL的LIMIT,因此其行为取决于下载足够数据后创建的提取作业数量。引用:

  

调用限制将允许Spark跳过从中读取一些部分   基础DataSource。这些将限制从中读取的数据量   Cassandra通过取消执行任务

可能的解决方案:

  • 可以通过限制numPartitions和数据汇率(concurrent.reads and other params)来部分管理DataFrame限制。如果你在大多数情况下你可以使用n~50“,你也可以限制where(dayIndex < 50 * factor * num_records)之类的内容。

  • 有一种方法可以将CQL LIMIT设置为SparkPartitionLimit,这会直接影响每个CQL请求(see more) - 请记住,请求是每个火花 - 划分。它在CassandraRdd扩展类中可用,因此您必须先转换为RDD。

代码如下:

filteredDataFrame.rdd.asInstanceOf[CassandraRDD].limit(n).take(n).collect()

这会将LIMIT $N附加到每个CQL请求。与DataFrame的限制不同,如果多次指定CassandraRDD limit.limit(10).limit(20)) - 只会追加最后一个。此外,我使用n而不是n / numPartitions + 1(因为它(即使Spark和Cassandra分区是一对一的)可能会返回每个分区更少的结果。因此,我必须添加take(n)才能将<= numPartitions * n缩减为n

警告仔细检查您的where是否可以转换为CQL(使用explain()) - 否则将在过滤前应用LIMIT

P.S。您还可以尝试使用sparkSession.sql(...)like here)直接运行CQL并比较结果。