代码之家  ›  专栏  ›  技术社区  ›  Adam Z

在cassandra数据建模中使用timeUUID作为主键

  •  0
  • Adam Z  · 技术社区  · 2 年前

    我们需要在cassandra中记录时间序列数据

    以下数据建模能够满足功能需求。

    CREATE TABLE sample_times (c timeuuid ,a varchar, PRIMARY KEY (c));
    INSERT INTO sample_times (c,a) VALUES ( now(), 'course1');
    INSERT INTO sample_times (c,a) VALUES ( now(), 'course2');
    INSERT INTO sample_times (c,a) VALUES ( now(), 'course3');
    INSERT INTO sample_times (c,a) VALUES ( now(), 'course0');
    INSERT INTO sample_times (c,a) VALUES ( now(), 'course5');
    INSERT INTO sample_times (c,a) VALUES ( now(), 'course4');
    INSERT INTO sample_times (c,a) VALUES ( now(), 'course0');
    

    要选择数据,我们可以通过一个小范围来选择它,在这个范围内只能找到有限的行数(即5分钟)。

    SELECT * FROM sample_times
       WHERE c > maxTimeuuid('2023-05-03 00:05+0000')
       AND c < minTimeuuid('2023-05-03 00:10+0000') allow filtering;
    

    但每次执行此操作时,我们都必须添加一个允许筛选,这意味着该子句可能会对性能产生影响。 我们不想添加一些其他主键,如workstation_id等来对时间序列数据进行分组,因为这不方便。

    我不知道cassandra在实现中是如何处理这一问题的。使用时间uuid作为大表中的第一个主键在性能上实用吗?

    谢谢~

    期望可以通过提供小的时间范围在大的表中快速地选择结果。

    0 回复  |  直到 2 年前
        1
  •  2
  •   Erick Ramirez    2 年前

    问题不在于timeUUID是否是分区键的好选择。问题是,使用分区键上的范围查询从Cassandra读取数据是否是个好主意,答案是否定的。

    你选择卡桑德拉是因为你有一个规模问题。为了解决规模问题,您必须通过将数据行聚类到分区来优化读取,这样您只需要读取一个分区就可以获得所需的所有数据。

    分区键上的范围查询不会扩展,因为根据定义,Cassandra必须向多个节点发送多个请求,以检索满足范围所需的每个分区——这是一种分散/聚集访问模式。

    你还没有发布很多关于你的用例的细节,但在我看来,你所拥有的并不是时间序列。你通常不会在时间序列中存储关于时间的数据——你存储的是关于某个包含时间成分的实体的数据。

    例如,以下是测量温度的设备的数据模型:

    CREATE TABLE devices (
        device_id text,
        time_recorded timestamp,
        temperature int,
        PRIMARY KEY (device_id, time_recorded)
    ) WITH CLUSTERING ORDER BY (time_recorded DESC)
    

    然后,您可以通过以下方式获取设备记录的温度范围:

    SELECT temperature FROM devices
        WHERE device_id = ?
        AND time_recorded > ?
        AND time_recorded < ?
    

    使用这个模型,您只需要从一个分区中检索数据,因此速度会非常快。干杯

        2
  •  1
  •   Aaron    2 年前

    问题是,使用分区键上的范围查询从Cassandra读取数据是否是个好主意,答案是否定的。

    Erick用这个一针见血。如果您确实需要将数据保存在 sample_times 到时间,我建议使用时间桶。

    CREATE TABLE sample_times (
        month_bucket int,
        c timeuuid,
        a varchar,
        PRIMARY KEY (month_bucket,c));
    
    INSERT INTO sample_times (month_bucket,c,a) VALUES (202305, now(), 'course1');
    INSERT INTO sample_times (month_bucket,c,a) VALUES (202305, now(), 'course2');
    INSERT INTO sample_times (month_bucket,c,a) VALUES (202305, now(), 'course3');
    INSERT INTO sample_times (month_bucket,c,a) VALUES (202305, now(), 'course0');
    INSERT INTO sample_times (month_bucket,c,a) VALUES (202305, now(), 'course5');
    INSERT INTO sample_times (month_bucket,c,a) VALUES (202305, now(), 'course4');
    INSERT INTO sample_times (month_bucket,c,a) VALUES (202305, now(), 'course0');
    

    一旦我完成了这项工作,那么这将通过查询单个分区来实现:

    > SELECT * FROM sample_times WHERE month_bucket=202305
         AND c > maxTimeuuid('2023-05-04 13:15+0000')
         AND c < minTimeuuid('2023-05-04 13:20+0000') ;
    
     month_bucket | c                                    | a
    --------------+--------------------------------------+---------
           202305 | acedca70-ea7d-11ed-a0c5-ebdd5e669579 | course1
           202305 | acee66b0-ea7d-11ed-a0c5-ebdd5e669579 | course2
           202305 | aceedbe0-ea7d-11ed-a0c5-ebdd5e669579 | course3
           202305 | acef2a00-ea7d-11ed-a0c5-ebdd5e669579 | course0
           202305 | acef9f30-ea7d-11ed-a0c5-ebdd5e669579 | course5
           202305 | acefed50-ea7d-11ed-a0c5-ebdd5e669579 | course4
           202305 | ad6a7480-ea7d-11ed-a0c5-ebdd5e669579 | course0
    
    (7 rows)
    

    这里需要关注的主要问题是分区的大小。Cassandra的硬限制是每个分区20亿个单元,但总的来说,您希望将分区保持在100MB以下。基本上,“月”可能不是一个足够小的桶,这取决于每月发生的写入次数。如果有很多,那么可能需要一周甚至一天的时间。