为日期查询构建 Cassandra table

structuring Cassandra table for date queries

我正在学习 Cassandra,作为练习数据集,我正在从 Yahoo 抓取历史股票数据。每个交易日将有一条记录。

显然,我需要将股票代码作为分区键的一部分。关于我是应该将日期作为分区键的一部分,还是应该将其作为集群列,我看到了相互矛盾的信息?

实际上,股票市场每年开市约 253 天。因此,一只股票每年将有约 253 条记录。我不是在构建一个完整的数据库,而是想设计它以适应/正确。

如果我将日期作为分区键的一部分,它不会分布在多个节点上吗?使日期范围查询变慢?

If I make the date part of the partition key, won't that be possibly be spread across nodes? Make a date range query slow?

是的,两个帐户都是正确的。这种建模方法称为“时间分桶”,其主要用例是 time/event 随时间增长的数据。好消息是,您不需要这样做,除非您的分区预计会变大。根据您当前的预测,每年每个分区写入 253 行,每年只会小于 40kb(参见下面 nodetool tablehistograms 的计算)。

出于您的目的,我认为按 symbol 进行分区并按 day 进行聚类就足够了。

CREATE TABLE stockquotes (
 symbol text,
 day date,
 price decimal,
 PRIMARY KEY(symbol, day))
 WITH CLUSTERING ORDER BY (day DESC);

对于大多数 time-based 用例,我们更倾向于关注最近的数据(您的案例可能是这样,也可能不是这样)。如果是这样,那么按 day 降序写入数据将提高这些查询的性能。

然后(在写入一些数据之后),像这样的日期范围查询将起作用:

SELECT * FROM stockquotes 
WHERE symbol='AAPL'
  AND day >= '2020-08-01' AND day < '2020-08-08';

 symbol | day        | price
--------+------------+--------
   AAPL | 2020-08-07 | 444.45
   AAPL | 2020-08-06 | 455.61
   AAPL | 2020-08-05 | 440.25
   AAPL | 2020-08-04 | 438.66
   AAPL | 2020-08-03 | 435.75

(5 rows)

要验证分区大小可以使用 nodetool tablehistograms(一旦数据刷新到磁盘)。

bin/nodetool tablehistograms Whosebug.stockquotes
Whosebug/stockquotes histograms
Percentile      Read Latency     Write Latency          SSTables    Partition Size        Cell Count
                    (micros)          (micros)                             (bytes)
50%                     0.00              0.00              0.00               124                 5
75%                     0.00              0.00              0.00               124                 5
95%                     0.00              0.00              0.00               124                 5
98%                     0.00              0.00              0.00               124                 5
99%                     0.00              0.00              0.00               124                 5
Min                     0.00              0.00              0.00               104                 5
Max                     0.00              0.00              0.00               124                 5

每年分区大小 = 124 字节 x 253 = 31kb

鉴于分区大小很小,此模型可能适合 至少 30 年的数据,然后再 slow-down(我建议保持分区 <= 1mb) .也许像 quartercentiry 这样的东西就足够了?不管怎样,短期内还是可以的。

编辑:

Seems like any date portion used in the PK would spread the data across nodes, no?

是的,分区键中使用的日期部分会跨节点传播数据。这实际上就是这样做的意义所在。您不希望以未绑定的行增长 anti-pattern 结束,因为分区最终会变得太大以至于无法使用。这个想法就是为了确保足够的数据分布。

lets say 1/sec and I need to query across years, etc. How would that bucketing work?

所以时间分桶的诀窍是在数据分布和查询灵活性之间找到一个“平衡点”。不幸的是,可能会出现查询会命中多个分区(节点)的边缘情况。但我们的想法是建立一个模型来很好地处理其中的大部分问题。

这里的示例问题是一年每秒 1 次,有点极端。但是解决的思路是一样的。一天有86400秒。根据行的大小,这甚至可能每天都太多了。但是为了争论,说我们可以。如果我们 bucket on day,PK 是这样的:

PRIMARY KEY ((symbol,day),timestamp)

WHERE 子句开始看起来像这样:

WHERE symbol='AAPL' AND day IN ('2020-08-06','2020-08-07');

另一方面,几天没问题,但查询一整年就很麻烦了。此外,我们不想构建 253 天的 IN 子句。事实上,我不建议人们在 IN.

上超过个位数

一种可能的方法是从应用程序中触发 253 个异步查询(每天一个),然后 assemble 并在那里对结果集进行排序。使用 Spark(在 RDD 中做所有事情)也是一个不错的选择。实际上,Cassandra 并不是用于报告的出色数据库 API,因此探索一些其他工具很有价值。