如何在优化速度的同时处理 Cassandra 中的时间维度(历史)?

How to deal with the dimension of time (historic) in Cassandra while optimising for speed?

我们正在处理这样一种情况,即我们存储具有 X 数量属性的项目(这是一个 SaaS 解决方案,每个实例都有不同数量的属性)。我们纠结的是时间维度

如果我们希望能够:

,最好的数据存储方式是什么?
  1. 快速获取单个项目。
  2. 获取具有特定时间戳(即历史信息)的 属性 的值。

注意:我们不想搜索 属性 值,我们想要速度 :-)尽快获取。

SaaS 解决方案的示例用例:我们有一艘装有 10.000 个传感器的船,它们每分钟收集一次温度。这意味着我们有 10.000 "items",其中 "temperature" 作为属性之一。它们将每分钟更新一次,我们希望存储历史记录。

选项 1. 全部存储在映射中(Id = 主键)

------------------------------------------------
Id | Name | Props
------------------------------------------------
1  | Foo  | map<timestamp, map<name, text>>
------------------------------------------------
2  | Bar  | map<timestamp, map<name, text>>
------------------------------------------------

在地图中我们会有类似的东西:

{
    "1518023285": {
        "propName": "Prop A",
        "propValue": "Value A"
    },
    "1518011111": {
        "propName": "Prop A",
        "propValue": "Value B"
    },
    "1518011111": {
        "propName": "Prop B",
        "propValue": "Value C"
    }
}

Prop AProp B 同时创建,Prop A 已更新。

我们将收集完整的项目并使用我们的应用程序在正确的时间找到正确的价值。

选项 2. 将时间存储在地图和道具中作为行(Id = 主键)

-----------------------------------------------------------
Id | Name | Prop_A               | Prop_B
-----------------------------------------------------------
1  | Foo  | map<timestamp, text> | map<timestamp, text>
-----------------------------------------------------------
2  | Bar  | map<timestamp, text> | map<timestamp, text>
-----------------------------------------------------------

Prop_A 列中,我们将有类似的内容:

{
    "1518023285": "Value B",
    "1518011111": "Value A"
}

意思是 Prop_A 是用 Value A 创建的,后来用 Value B 更新了。

我们将收集完整的项目并使用我们的应用程序在正确的时间找到正确的价值。

方案3.一个map中的属性和一行中的时间(Id=Primary Key,ItemId有index,Time有index)

-------------------------------------------------
Id | ItemId | Name | Time       | Props
-------------------------------------------------
1  | 1      | Foo  | 1518011111 | map<name, text>
-------------------------------------------------
2  | 2      | Bar  | 1518011111 | map<name, text>
-------------------------------------------------
3  | 2      | Bar  | 1518023285 | map<name, text>
-------------------------------------------------

地图将如下所示:

{
    "Prop A": "Value A",
    "Prop B": "Value B"
}

我们将收集 所有 行项目并在我们的应用程序中找到合适的时间

选项4.连续属性和时间(Id=Primary Key,ItemId有索引,Time有索引)

----------------------------------------------------
Id | ItemId | Name | Time       | Prop_A   | Prop_B
----------------------------------------------------
1  | 1      | Foo  | 1518011111 | Value A  | Value B
----------------------------------------------------
2  | 2      | Bar  | 1518011111 | Value A  | Value B
----------------------------------------------------
3  | 2      | Bar  | 1518023285 | Value A  | Value C
----------------------------------------------------

第 3 行已更新。

我们创建了 2 个 CQL 查询,一个用于查找最新版本,另一个用于收集道具。

CQL 集合(有一些例外)完全反序列化到内存中,从长远来看这可能非常糟糕。特别是从性能的角度来看,它不太理想,它们是为了方便使用较小的地图,而不是性能。

我实际上会推荐类似选项 4 的内容,例如:((id, item_id), name, time, prop) 其中 prop 可以是 "A" 或 "B" 以及一个值字段。如果 "prop" 真的仅限于 A-C 之类的东西,可以切换时间和道具,这样你就可以查询每个 属性 的时间线,然后将几个查询合并在一起。请务必更改时间顺序,以便最近的数据位于分区的开头,以便更有效地读取最新值。如果有大量的插入,你会想要更多地分解分区,可能包括一个 "year-month" 到你的分区键。

我会选择选项 3,但对 Chris 的提议进行了类似的更改:

((id, item_id), time, name, map)

如果地图在每个时间戳中都没有变化(意味着它们对于该时间戳是只读的),我认为利用集合没有任何缺点。它还将为您节省一些磁盘 space,所有属性都在一个地图中,而不是将它们放在不同的列中。