【问题标题】:simplest example of a query by date range in cassandra 1.xcassandra 1.x 中按日期范围查询的最简单示例
【发布时间】:2024-01-19 11:46:01
【问题描述】:

我想存储一个 ID 和一个日期,我想检索从 dateA 到 dateB 的所有条目,我究竟需要什么才能执行select from my_column_family where date >= dateA and date < dateB;

【问题讨论】:

标签: nosql cassandra


【解决方案1】:

#cassandra (IRC) 的人帮我找到了一个方法,有很多微妙的细节,所以我想在这里记录一下。

首先你需要声明一个与此类似的列族(来自 cassandra-cli 的示例):

create column family users with comparator=UTF8Type and key_validation_class=UTF8Type and column_metadata=[
    {column_name: id, validation_class: LongType}
    {column_name: name, validation_class: UTF8Type, index_type: KEYS}
    {column_name: age, validation_class: LongType}
];

关于此声明的一些重要事项:

  • 比较器和 key_validation_class 可以将字符串用作键名
  • 第一个声明的列是特殊的,它是用于寻址每一行的“行键”,因此不能包含重复值(INSERT 实际上是一个 UPSERT,所以当有重复值时,新值会覆盖旧值)李>
  • 第二列在其值上声明了一个“二级索引”(更多内容见下文)
  • 日期存储为 Long 数据类型,解释由客户决定

现在让我们添加一些值:

set users[1][name] = john;
set users[1][age]  = 19;
set users[2][name] = jane;
set users[2][age]  = 21;
set users[3][name] = john;
set users[3][age]  = 32;

据此:http://pkghosh.wordpress.com/2011/03/02/cassandra-secondary-index-patterns/ Cassandra 不支持 < 运算符,它所做的是手动排除不匹配的行,但它会在有结果集之后这样做并且它也拒绝这样做,除非并进行了实际过滤。

这意味着像get users where age > 20; 这样的查询将返回null,但如果我们添加一个包含= 的谓词,它就会神奇地起作用。

这里是二级索引很重要的地方,没有它你不能使用= 所以在这个例子中我可以使用get users where name = jane; 但我不能要求get users where age = 21;

有趣的是,在使用= 之后,< 可以正常工作,因此拥有二级索引可以让您请求get users where name = john and age > 20;,它会正确过滤。

【讨论】:

  • 这是解决问题的一种方法,但是它依赖于二级索引列上的重复值,这可能不是最好的主意;我很想听听替代方案并讨论每种方案的优缺点。有人告诉我,这也可以使用有序分区器中的 TimeUUID 和 get_slice 来完成,但我不知道该怎么做,而且我读到有序分区器不擅长平衡。
  • 这也不适用于超级列,因为它们不支持二级索引。
  • 实际上,只有 = 对二级索引起作用这一事实并不神奇,这是因为索引的性质不是 BTree。
【解决方案2】:

有几种方法可以解决这个问题。最简单的可能是您自己的答案中提到的具有相等限制的二级索引解决方案。我使用了这种方法,添加了一个名为 'valid' 的附加列,将值设置为 1。然后查询可以变为 where valid=1 and date>nnnn

其他解决方案需要额外的列族和额外的查询。

在加载数据时,创建并添加一个包含时间戳作为键的列族,每个条目都会列出所有用户 ID 作为列名。

如果分区策略是有序的,那么单个RangeSliceQuery可以将日期范围指定为键范围,并获取每个键的所有列。然后遍历结果键,使用每个用户 ID 的列值,如果需要,查询原始列族以获取与每个 ID 关联的数据。 Cassandra 总是按顺序存储列名,读取时可以颠倒。

但是,正如documented,有序分区器并不理想,导致热点和节点负载均衡困难。

如果没有有序的分区程序,仍然保留时间戳列族,您将不得不在加载数据时创建另一个列族,您可以将所有时间戳存储为一个或多个已知键下的列(例如“创建”或“更新” ')。第一个查询将是一个已知键的 SliceQuery,然后列名(作为时间戳)将提供 MultigetSliceQuery 的键em> 到时间戳列族。

我对此使用了变体,通常添加复合键或列以增加灵活性。

【讨论】:

  • 我真的需要添加一个虚拟列并在每次插入中将其设置为 1,以便能够按范围过滤完全不相关的其他列吗?那里的工具对我不利...
  • 我相信只有一个二级索引.. 但是.. 看看the answer 一个完全不相关的问题,以了解 CQL3 如何利用复合来解决您的问题。有关详细信息,请参阅CQL#select
  • 我读到如果我们将列用作复合主键的一部分,则可以有效地执行范围查询,但这仅适用于推荐的有序分区器,因为它会破坏分片策略...使用虚拟列并不好。它需要全表扫描,因此在大型数据集上非常慢。是否有其他适用于 Cassandra 中大型分布式数据集的范围过滤解决方案?
  • 列总是按顺序存储的,所以如果列名是复合的,你可以通过 ColumnSliceIterator 等获得有效的查询。如果行键是复合的,那么分区器会决定。在 CQL 示例中,请注意主键与行键不同。