我们正在使用Cassandra DataStax 6.0和启用了Spark。我们每天都会有10GB的数据。所有查询均基于日期。我们有一张巨大的桌子,有40列。我们计划使用Spark生成报告。设置此数据的最佳方法是什么。由于我们每天都在获取数据,并将数据保存在一张表中大约一年。
我们尝试使用不同的分区,但是我们的大多数键都是基于日期的。
没有代码只需要建议
我们的查询应该足够快。我们有9个节点的256GB Ram。 44核CPU。
答案 0 :(得分:1)
将数据组织在日常分区中并不是一个很好的设计-在这种情况下,只有RF节点在白天写入数据时才处于活动状态,然后在生成报表时就处于活动状态。
由于您将仅从Spark访问数据,因此可以使用以下方法-将一些存储桶字段用作分区键,例如,使用统一生成的随机数,并将时间戳作为聚类列,也许还有{ 1}}列,以确保记录的唯一性,例如:
uuid
应该选择create table test.sdtest (
b int,
ts timestamp,
uid uuid,
v1 int,
primary key(b, ts, uid));
的生成最大值,以使分区没有太大和很小,以便我们可以有效地读取它们。
然后我们可以像这样运行Spark代码:
b
这里的窍门是我们使用随机分区键在节点之间分配数据,因此所有节点将在写入数据和生成报告期间处理负载。
如果我们研究一下该Spark代码的物理计划(为便于阅读而格式化):
import org.apache.spark.sql.cassandra._
val data = spark.read.cassandraFormat("sdtest", "test").load()
val filtered = data.filter("ts >= cast('2019-03-10T00:00:00+0000' as timestamp) AND ts < cast('2019-03-11T00:00:00+0000' as timestamp)")
我们可以看到,这两个条件都将被推到CQL级别的DSE上-这意味着Spark不会将所有数据加载到内存中并对其进行过滤,而是所有过滤都将在Cassandra中进行,而只有必要的数据会返回。而且由于我们在多个节点之间分散请求,因此读取(比对测试)可能比读取一个巨型分区更快(需要测试)。这种设计的另一个好处是,使用Spark可以很容易地执行删除旧数据的操作,如下所示:
== Physical Plan ==
*Scan org.apache.spark.sql.cassandra.CassandraSourceRelation [b#23,ts#24,v1#25]
PushedFilters: [*GreaterThanOrEqual(ts,2019-03-10 00:00:00.0),
*LessThan(ts,2019-03-11 00:00:00.0)], ReadSchema: struct<b:int,ts:timestamp,v1:int>
在这种情况下,Spark将执行非常有效的范围/行删除,这将产生更少的逻辑删除。
P.S。建议使用DSE的Spark连接器版本,因为它可能会有更多优化。
P.P.S。从理论上讲,我们可以将val toDel = sc.cassandraTable("test", "sdtest").where("ts < '2019-08-10T00:00:00+0000'")
toDel.deleteFromCassandra("test", "sdtest", keyColumns = SomeColumns("b", "ts"))
和ts
合并到一个uid
列中,但是我不确定它是否可以与Dataframes一起使用。