【发布时间】:2014-10-16 11:06:12
【问题描述】:
免责声明:
这是一个相当长的帖子。我首先解释我正在处理的数据,以及我想用它做什么。
然后我详细介绍了我考虑过的三种可能的解决方案,因为我已经尝试过做功课(我发誓:])。我最终得到一个“最佳猜测”,这是第一个解决方案的变体。
我的终极问题是:使用 Cassandra 解决我的问题的最明智的方法是什么?这是我的尝试之一,还是别的什么?
我正在寻找经验丰富的 Cassandra 用户的建议/反馈...
我的数据:
我有许多超级文档,它们拥有树结构中的文档(标题、副标题、部分……)。
每个 SuperDocument 结构都可以随着时间的推移而改变(主要是标题的重命名),从而为我提供了多个版本的结构,如下所示。
我在寻找什么:
对于每个 SuperDocument,我需要按上述日期为这些结构加上时间戳,并且我希望在给定的日期找到最接近的 SuperDocument 结构的早期版本。 (即version_date < given_date 的最新版本)
这些注意事项可能有助于更轻松地解决问题:
- 版本是不可变的:更改很少见,每次更改时我都可以创建整个结构的新表示。
- 我不需要访问结构的子树。
- 我想说我不需要找到给定叶子的所有祖先,也不需要访问树中的特定节点/叶子。拥有整棵树后,我可以在客户端代码中解决所有这些问题。
好的,让我们开始吧
请记住,我真的才刚刚开始使用 Cassandra。我已经阅读/观看了很多有关数据建模的资源,但在该领域没有太多(任何!)经验!
这也意味着一切都将用 CQL3 编写......对不起 Thrift 爱好者!
我第一次尝试解决这个问题是创建下表:
CREATE TABLE IF NOT EXISTS superdoc_structures (
doc_id varchar,
version_date timestamp,
pre_pos int,
post_pos int,
title text,
PRIMARY KEY ((doc_id, version_date), pre_pos, post_pos)
) WITH CLUSTERING ORDER BY (pre_pos ASC);
这会给我以下结构:
我在这里为我的树使用Nested Sets model;我认为保持结构有序会很好,但我愿意接受其他建议。
我喜欢这个解决方案:每个版本都有自己的行,其中每一列代表层次结构的一个级别。
但问题是我(坦率地)打算按如下方式查询我的数据:
SELECT * FROM superdoc_structures
WHERE doc_id="3399c35...14e1" AND version_date < '2014-03-11' LIMIT 1
Cassandra 很快提醒我,我不被允许这样做! (因为分区器不保留集群节点上的行顺序,所以无法扫描分区键)
然后...?
好吧,因为 Cassandra 不允许我在分区键上使用不等式,所以就这样吧!
我会让version_date 成为一个集群键,我所有的问题都会消失。是的,不是真的……
第一次尝试:
CREATE TABLE IF NOT EXISTS superdoc_structures (
doc_id varchar,
version_date timestamp,
pre_pos int,
post_pos int,
title text,
PRIMARY KEY (doc_id, version_date, pre_pos, post_pos)
) WITH CLUSTERING ORDER BY (version_date DESC, pre_pos ASC);
我觉得这个不太优雅:所有版本的和结构级别都被制成现在非常宽的行的列(与我之前的解决方案相比):
问题:对于同样的请求,使用LIMIT 1 只会返回第一个标题。并且不使用 LIMIT 将返回所有版本的结构级别,我必须对其进行过滤以仅保留最新的。
第二次尝试:
还没有第二次尝试...虽然我有一个想法,但我觉得它没有明智地使用 Cassandra。
我们的想法是仅按version_date 进行聚类,并且以某种方式将整个层次结构存储在每个列值中。听起来是不是很糟糕?
我会这样做:
CREATE TABLE IF NOT EXISTS superdoc_structures (
doc_id varchar,
version_date timestamp,
nested_sets map<int, int>,
titles list<text>,
PRIMARY KEY (doc_id, version_date)
) WITH CLUSTERING ORDER BY (version_date DESC);
生成的行结构将是:
实际上对我来说看起来还不错,但我可能会有比级别标题更多的数据来反规范化到我的列中。如果它只有两个属性,我可以使用另一个地图(例如,将标题与 id 关联),但更多的数据会导致更多的列表,我觉得它很快就会成为一种反模式。
另外,当数据进来时,我必须在我的客户端应用程序中将所有列表合并在一起!
另类和最佳猜测
在考虑了更多之后,有一个“混合”解决方案可能有效并且可能高效且优雅:
我可以使用另一个表,该表仅列出 SuperDocument 的版本日期,并将这些日期缓存到 Memcache 实例(或 Redis 或其他)中以便真正快速访问。
这将使我能够快速找到我需要获取的版本,然后使用我的第一个解决方案的复合键请求它。
这是两个查询,加上一个要管理的内存缓存存储。但无论如何我最终可能会得到一个,所以也许这是最好的妥协?
也许我什至不需要缓存存储?
总而言之,我真的觉得第一个解决方案是对我的数据建模的最优雅的解决方案。你呢?!
【问题讨论】:
标签: cassandra data-modeling hierarchical-data cassandra-2.0