首页 文章

Cassandra - 重叠数据范围

提问于
浏览
4

我在Cassandra中有以下'Tasks'表 .

  • Task_ID UUID - 分区键

  • Starts_On TIMESTAMP - 聚类列

  • Ends_On TIMESTAMP - 聚类列

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

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

3 回答

  • 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)
    

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

    NOTES:

    • 如果您需要查询事件是否开始或结束(我没有),您需要在主键的 eventTime 之前订购 eventType .

    • 您将存储两次事件(一次为开头,一次为结束) . 在Cassandra中,重复数据通常不是很重要,但我确实想明确指出这一点 .

    • 在您的情况下,您将需要找到一个好的分区键,因为 Task_ID 将太独特(高基数) . 这是Cassandra必须的,因为您无法对分区键(仅限聚类键)进行查询 .

  • 2

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

  • 0

    这是另一个想法(有些不同寻常) . 您可以创建用户定义的函数来实现第二个范围过滤器(在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
    

    现在让我们得到开始> = 2且结束<= 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> = 2的过滤器在将其传递给UDF之前删除了一行 .

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

相关问题