我有大型的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)
的情况下运行此代码和代码的时间大致相同。
答案 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并比较结果。