【问题标题】:Cassandra - Overlapping Data RangesCassandra - 重叠数据范围
【发布时间】:2023-03-29 15:18:01
【问题描述】:

我在 Cassandra 中有以下“任务”表。

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

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

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

【问题讨论】:

    标签: cassandra cql cassandra-2.0 cql3


    【解决方案1】:

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

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

    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 和 end

    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 之前删除了一行。

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

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 谢谢吉姆。但在我的情况下,即使在starts_on 上进行范围查询后,也会产生很多我必须在客户端过滤的记录,这是我试图减少的。到目前为止,我看到的唯一解决方案是拥有另一个包含重复数据的表,但将 ends_on 作为集群列,然后在应用程序中合并结果。
      • 我认为这种方法会比我建议的要慢,因为您将读取更多行(读取每个所需行两次,加上拒绝)。另一种方法是使用 spark,它可以对多个字段进行范围查询,因为它支持更完整的 SQL 实现。
      【解决方案3】:

      不久前,我编写了一个应用程序,它在查询具有开始时间和结束时间的事件时遇到了类似的问题。对于我们的场景,我能够对用户 ID 进行分区(因为查询是针对特定用户的事件),为事件类型和事件日期设置聚类列。表结构如下所示:

      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 中的必须,因为您不能对分区键(仅集群键)进行范围查询。

      【讨论】:

      • 感谢您的详细回答。但是我的问题略有不同,因为我在要查询的表中存储了两个时间戳字段(starts_on 和 ends_on)。在 SQL 世界中,我会在这两个字段上使用范围查询,但 Cassandra 不允许在集群列上使用它。
      【解决方案4】:

      在 Cassandra 中似乎没有完全令人满意的方法来执行此操作,但以下方法似乎效果很好:

      我按降序对Starts_On 时间戳上的表进行聚类。 (Ends_On 只是一个常规列。)然后我使用 Starts_On&lt;? 来约束查询,其中参数是感兴趣期的结束 - 即过滤掉在我们感兴趣的期结束后开始的事件。

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

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

      理想情况下,您希望分页大小略大于您希望收到的相关行总数。如果分页大小太小,驱动程序最终会检索多个页面,这可能会损害性能。如果它太大,您最终会丢弃很多行,这可能会因传输过多的数据而影响性能。在实践中,您可能会找到一个很好的折衷方案。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-05-19
        • 1970-01-01
        • 2018-05-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多