Oli*_*nce 10 data-modeling hierarchical-data cassandra cassandra-2.0
免责声明:
这是一篇相当长的帖子.我首先解释我正在处理的数据,以及我想用它做什么.
然后我详细介绍了我考虑过的三种可能的解决方案,因为我已经尝试过做作业了(我发誓:]).我最终得到了"最佳猜测",这是第一个解决方案的变体.
我的终极问题是:使用Cassandra解决问题最明智的方法是什么?这是我的尝试之一,还是别的什么?
我正在寻找经验丰富的Cassandra用户的建议/反馈......
我的数据:
我有很多SuperDocuments在树形结构(标题,副标题,部分......)中拥有文档.
每个SuperDocument结构都可以随着时间的推移而改变(主要是重命名标题),从而为我提供了多个版本的结构,如下所示.

我正在寻找:
对于每个SuperDocument我需要按照上面的日期对这些结构加时间戳,并且我希望在给定的日期找到最接近的早期版本的SuperDocument结构.(即最新版本version_date < given_date)
这些考虑可能有助于更轻松地解决问题:
好吧,让我们这样做
请记住我真的只是开始使用Cassandra.我已经阅读/观看了很多关于数据建模的资源,但是在该领域没有太多(任何!)经验!
这也意味着一切都将用CQL3编写...对不起节俭爱好者!
我第一次尝试解决这个问题是创建下表:
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);
Run Code Online (Sandbox Code Playgroud)
这会给我以下结构:

我在这里使用嵌套集模型 ; 我认为保持结构有序会很好,但我对其他建议持开放态度.
我喜欢这个解决方案:每个版本都有自己的行,其中每列代表层次结构的级别.
但问题是我(坦率地)打算查询我的数据如下:
SELECT * FROM superdoc_structures
WHERE doc_id="3399c35...14e1" AND version_date < '2014-03-11' LIMIT 1
Run Code Online (Sandbox Code Playgroud)
卡桑德拉很快提醒我,我不被允许这样做!(因为分区程序不保留群集节点上的行顺序,因此无法扫描分区键)
然后怎样呢...?
好吧,因为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);
Run Code Online (Sandbox Code Playgroud)
我发现这个不太优雅:所有版本和结构级别都被制成一个非常宽的行的列(与我之前的解决方案相比):

问题:使用相同的请求,使用LIMIT 1只会返回第一个标题.并且使用no 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);
Run Code Online (Sandbox Code Playgroud)
结果行结构将是:

事实上它看起来对我来说没问题,但是我可能会有更多的数据而不是级别标题来反规范化到我的列中.如果它只有两个属性,我可以使用另一个地图(例如将标题与ID相关联),但更多的数据会导致更多的列表,我觉得它很快就会变成反模式.
另外,当数据进入时,我必须在我的客户端应用程序中将所有列表合并在一起!
替代方案和最好的关系
在给予更多考虑之后,有一种"混合"解决方案可能会有效并且可能高效而优雅:
我可以使用另一个表,它只列出SuperDocument的版本日期并将这些日期缓存到Memcache实例(或Redis或其他)中以实现真正的快速访问.
这将允许我快速找到我需要获取的版本,然后使用我的第一个解决方案的复合键请求它.
这是两个查询,加上要管理的内存缓存存储.但无论如何我可能会最终得到一个,所以也许这是最好的折衷方案?
也许我甚至不需要缓存商店?
总而言之,我真的觉得第一个解决方案是对我的数据建模最优雅的解决方案.你呢?!
首先,您不需要使用 memcache 或 redis。Cassandra 将使您能够非常快速地访问该信息。您当然可以拥有一张类似于以下内容的表:
create table superdoc_structures {
doc_id varchar;
version_date timestamp;
/* stuff */
primary key (doc_id, version_date)
} with clustering order by (version_date desc);
Run Code Online (Sandbox Code Playgroud)
这将为您提供一种快速访问给定版本的方法(此查询可能看起来很熟悉 ;-):
select * from superdoc_structures
where doc_id="3399c35...14e1" and
version_date < '2014-03-11'
order by version_date desc
limit 1;
Run Code Online (Sandbox Code Playgroud)
由于从架构的角度来看,文档树结构似乎没有任何相关性,并且每次有新版本时,您都乐于创建完整的文档,我不明白为什么您甚至麻烦把树分解成单独的行。为什么不将表格中的整个文档作为文本或 blob 字段?
create table superdoc_structures {
doc_id varchar;
version_date timestamp;
contents text;
primary key (doc_id, version_date)
} with clustering order by (version_date desc);
Run Code Online (Sandbox Code Playgroud)
因此,要获取新年时存在的文档内容,您需要执行以下操作:
select contents from superdoc_structures
where doc_id="...." and
version_date < '2014-01-1'
order by version_date > 1
Run Code Online (Sandbox Code Playgroud)
现在,如果您确实想要维护文档组件的某种层次结构,我建议您使用闭包表之类的方法来表示它。或者,既然您愿意在每次写入时复制整个文档,为什么不在每次写入时复制整个部分信息,为什么不这样做并具有如下架构:
create table superdoc_structures {
doc_id varchar;
version_date timestamp;
section_path varchar;
contents text;
primary key (doc_id, version_date, section_path)
) with clustering order by (version_date desc, section_path asc);
Run Code Online (Sandbox Code Playgroud)
然后让部分路径具有类似“first_level next_level sub_level Leaf_name”的语法。作为附带的好处,当您拥有文档的 version_date 时(或者如果您在 section_path 上创建二级索引),因为空格在词法上比任何其他有效字符“低”,您实际上可以非常干净地抓取一个小节:
select section_path, contents from superdoc_structures
where doc_id = '....' and
version_date = '2013-12-22' and
section_path >= 'chapter4 subsection2' and
section_path < 'chapter4 subsection2!';
Run Code Online (Sandbox Code Playgroud)
或者,您可以使用 Cassandra 对集合的支持来存储这些部分,但同样......我不知道为什么你甚至会费心把它们分开,因为一大块就很好用。