Cassandra - 重叠数据范围

时间:2015-08-24 09:29:49

标签: cassandra cql cassandra-2.0 cql3

我有以下'任务'卡桑德拉的桌子。

  • Task_ID UUID - 分区键
  • Starts_On TIMESTAMP - 聚类列
  • Ends_On TIMESTAMP - 聚类列

我想运行CQL查询以获取给定日期范围的重叠任务。例如,如果我将两个时间戳(T1和T2)作为参数传递给查询,我想获得该范围内适用的所有任务(即重叠记录)。

Cassandra最好的办法是什么?我不能在Starts_On和Ends_On上使用两个范围,因为要向Ends_On添加范围查询,我必须对Starts_On进行相等性检查。

4 个答案:

答案 0 :(得分:2)

这是另一个想法(有些不同寻常)。您可以创建用户定义的函数来实现第二个范围过滤器(在Cassandra 2.2和更新版本中)。

假设您像这样定义表(用ints而不是时间戳显示以保持示例简单):

CREATE TABLE tasks (
    p int, 
    task_id timeuuid, 
    start int, 
    end int, 
    end_range int static, 
    PRIMARY KEY(p, start));

现在我们创建一个用户定义的函数来根据结束时间检查返回的行,并返回匹配行的task_id,如下所示:

CREATE FUNCTION my_end_range(task_id timeuuid, end int, end_range int) 
    CALLED ON NULL INPUT RETURNS timeuuid LANGUAGE java AS 
    'if (end <= end_range) return task_id; else return null;';

现在我在第三个参数中使用了一个技巧。在明显的(主要?)监督中,似乎您无法将常量传递给用户定义的函数。因此,为了解决这个问题,我们将静态列(end_range)作为常量传递。

首先,我们必须设置我们想要的end_range:

UPDATE tasks SET end_range=15 where p=1;

让我们说我们有这些数据:

SELECT * FROM tasks;

 p | start | end_range | end | task_id
---+-------+-----------+-----+--------------------------------------
 1 |     1 |        15 |   5 | 2c6e9340-4a88-11e5-a180-433e07a8bafb
 1 |     2 |        15 |   7 | 3233a040-4a88-11e5-a180-433e07a8bafb
 1 |     4 |        15 |  22 | f98fd9b0-4a88-11e5-a180-433e07a8bafb
 1 |     8 |        15 |  15 | 37ec7840-4a88-11e5-a180-433e07a8bafb

现在让我们获取start_> 2并且结束&lt; = 15的task_id:

SELECT start, end, my_end_range(task_id, end, end_range) FROM tasks 
    WHERE p=1 AND start >= 2;

 start | end | test.my_end_range(task_id, end, end_range)
-------+-----+--------------------------------------------
     2 |   7 |       3233a040-4a88-11e5-a180-433e07a8bafb
     4 |  22 |                                       null
     8 |  15 |       37ec7840-4a88-11e5-a180-433e07a8bafb

这样就可以为你提供匹配的task_id,你必须忽略空行(我还没有找到使用UDF删除行的方法)。您注意到start&gt; = 2的过滤器在将其传递给UDF之前删除了一行。

无论如何,显然不是一个完美的方法,但它可能是你可以使用的东西。 :)

答案 1 :(得分:0)

在CQL中,您一次只能对一个群集列进行查询,因此您可能需要在应用程序中进行某种客户端过滤。因此,您可以对starts_on进行范围查询,并在返回行时检查应用程序中的ends_on并丢弃您不想要的行。

答案 2 :(得分:0)

不久前,我在查询同时具有开始和结束时间的事件时编写了一个面临类似问题的应用程序。对于我们的场景,我能够对userID进行分区(因为查询是针对特定用户的事件),为事件类型设置了聚类列,也为事件日期设置了聚类列。表结构看起来像这样:

CREATE TABLE userEvents (
  userid UUID,
  eventTime TIMEUUID,
  eventType TEXT,
  eventDesc TEXT,
  PRIMARY KEY ((userid),eventTime,eventType));

通过这种结构,我可以userideventtime进行查询:

SELECT userid,dateof(eventtime),eventtype,eventdesc FROM userevents 
  WHERE userid=dd95c5a7-e98d-4f79-88de-565fab8e9a68 
  AND eventtime >= mintimeuuid('2015-08-24 00:00:00-0500');

 userid                               | system.dateof(eventtime) | eventtype | eventdesc
--------------------------------------+--------------------------+-----------+-----------
 dd95c5a7-e98d-4f79-88de-565fab8e9a68 | 2015-08-24 08:22:53-0500 |       End |    event1
 dd95c5a7-e98d-4f79-88de-565fab8e9a68 | 2015-08-24 11:45:00-0500 |     Begin |     lunch
 dd95c5a7-e98d-4f79-88de-565fab8e9a68 | 2015-08-24 12:45:00-0500 |       End |     lunch

(3 rows)

该查询将为我提供今天特定用户的所有事件行。

备注:

  • 如果您需要查询事件是否开始或结束(我没有),您需要在主键的eventType之前订购eventTime
  • 您将存储两次事件(一次为开头,一次为结束)。数据的重复通常不是Cassandra所关注的问题,但我确实想明确指出这一点。
  • 在您的情况下,您需要找到一个好的分区键,因为Task_ID太独特(高基数)。这是Cassandra中的必须,因为您无法对分区键(仅限聚类键)进行查询。

答案 3 :(得分:0)

在Cassandra中似乎没有一种完全令人满意的方法,但是以下方法似乎很好用:

我将表按Starts_On时间戳降序排列。 (Ends_On只是常规列。)然后我用Starts_On<?约束查询,其中参数是关注期的结束-即过滤掉在关注期结束后开始的事件。

然后我遍历结果,直到行Ends_On早于感兴趣时段的开始,然后丢弃其余的结果行。 (请注意,这是假设事件没有重叠-以后没有Ends_On的后续结果。)

丢弃其余的结果行可能看起来很浪费,但是这是关键所在:您可以将分页大小设置得足够小,以使要丢弃的行数相对较小,即使总行数非常大。

理想情况下,您希望分页的大小略大于您希望收到的相关行的总数。如果页面大小太小,驱动程序最终将检索多个页面,这可能会损害性能。如果太大,您最终将丢掉很多行,这又可能由于传输的数据超出必要而损害了性能。在实践中,您可能会找到一个不错的妥协。