【问题标题】:Cassandra time series data model with counter column带有计数器列的 Cassandra 时间序列数据模型
【发布时间】:2013-06-09 04:14:49
【问题描述】:

我正在尝试为时间序列建模 cassandra 数据集,其中我使用计数器列捕获给定用户在一分钟内的字节总和。使用 CQL3 我有这个创建表语法:

CREATE TABLE minute_usr (
min varchar,
usr varchar,
bytes counter,
PRIMARY KEY (min, usr)
)

我可以使用此查询更新列:

UPDATE minute_usr SET bytes = bytes + 200 WHERE usr = 'testuser' AND min = '1369448220';

但是我现在想在 usr = 'username' 的几分钟内取回 'bytes' 的值:

select min, bytes from minute_usr WHERE usr = 'testuser' AND min >= '1369448160' and min <= '1369448220';

我又得到了错误:

错误请求:随机分区器的分区键仅支持 EQ 和 IN 关系(除非您使用 token() 函数)

我假设 CQL 在创建表时使用“min”和“usr”创建了一个复合键,从而允许我获得一系列复合键。 cli 实际上告诉我,键实际上只是 'min' 的值:

[default@data_use] list minute_usr; 
Using default limit of 100 
Using default column limit of 100  
RowKey: 1369448220
=> (column=testuser:, value=, timestamp=1371066133370000)
=> (counter=testuser:bytes, value=1400)

 RowKey: 1369448160
=> (column=testuser:, value=, timestamp=1371066138506000)
=> (counter=testuser:bytes, value=1600)

我是否仅限于使用此数据模型对每个时间戳/用户名组合进行单独查询?

我还尝试使用用户名作为键的第一部分对数据进行建模,这样我就可以进行范围查询

CREATE TABLE usr_minute (
min varchar,
usr varchar,
bytes counter,
PRIMARY KEY (usr, min)
)

然后我可以对列名进行几分钟的范围查询,如下所示:

`select bytes from usr_minute WHERE usr = 'testuser' AND min >= '1369448160' and min <= '1369448220';`

但是我知道这些值现在存储在单行中,这在可以存储的列数方面存在限制,我希望让这个数据集永远增长。

不知道如何继续。

【问题讨论】:

    标签: cassandra data-modeling cql3


    【解决方案1】:

    如果你想要一个复合分区键,你需要额外的括号:

    CREATE TABLE minute_usr (
      min varchar,
      usr varchar,
      bytes counter,
      PRIMARY KEY ((min, usr))
    );
    

    但是,您不能对 min 或 usr 进行范围查询。您只能对非分区键进行范围查询。

    您可以使用第二个模型,将 usr 作为分区键,并在某个时间桶上进行分片以阻止行变得太大。例如,您可以每天为每个用户创建一个分区:

    CREATE TABLE usr_day_minute (
      day varchar,
      min varchar,
      usr varchar,
      bytes counter,
      PRIMARY KEY ((usr, day), min)
    );
    

    但现在,如果您想要多天的结果,则需要每天进行单独的查询。您可以选择更大的时间段,但代价是更大的行。

    请注意,您可能希望对 min 使用数字数据类型或时间戳,以便进行数字比较而不是字符串比较。

    【讨论】:

    • 使用此模型,您可以使用“in”查询,例如 select * from user_day_minute where usr = 'user1' and day in ('1','2','3',...) ;而不必运行单独的查询。我也支持数字类型的建议。
    • @Richard 你能帮我理解为什么我不能对像 min:username 这样的行键进行范围查询,我想从 1369448160:testuser 到 1369448220:testuser 之间的范围内返回字节列? Cassandra 的数据模型对我来说还没有完全理解。
    • 分区键(又名行键)是 Cassandra 用来在集群周围分区数据的键。使用 RandomPartitioner(默认和推荐的分区器),键是根据它们的散列分布的。因此,键以散列顺序而不是键顺序存储,因此范围查询效率极低,因此被禁用。但是,跨列(主键的非分区部分)的范围查询是有效的,建议使用。
    • @Richard 感谢您的解释,您提供的示例对理解数据模型非常有帮助。我想知道是否正在做一些工作来使用有序分区器更好地分配行,或者使用随机分区器按键顺序排序。
    【解决方案2】:

    我认为您真的不需要担心行大小。您可以将单个分区扩展到 20 亿个单元,因此您可能还可以。

    如果您真的认为数据分布不好,只需插入一个随机填充的枚举。

    create table network_usage (
        usr varchar,
        bucket_enum int,
        when timestamp,
        bytes counter,
        PRIMARY KEY ((usr, bucket), when)
    );
    

    现在,您可以从时间戳中的某个时间点派生 bucket_enum,但这意味着对于某些时间窗口,所有数据都将位于一个节点中,我想这将是一个问题。让 bucket_enum 的大小大致取决于您希望数据分片的程度。您可能只使用数据库中的分区数(并在运行时查询它)。

    然后,要更新消费信息,您可能会执行以下操作: #您选择的语言 ts = 现在() bucket = random_integer() % sharding_factor;

    #now in CQL
    consistency any;
    update network_usage set bytes = bytes + 200
        where usr = 'testuser' and bucket_enum = :bucket and when = now();
    

    存储桶实际上只是对 CQL 的一种滥用,以使主键跨数据库中的节点分片。现在我们在查询时利用它。假设分片因子为 6:

    #you may want a different consistency level, but since this is mostly historical data,
    #one should really be enough.
    consistency one;
    select count from network_usage
    where usr = 'testuser' AND
        bucket_enum in (0, 1, 2, 3, 4, 5) AND
        when >= :start_time and
        when < :end_time;
    

    与其他方法的不同之处在于,您可以准确控制数据分布的多少,您可以随时轻松地重新平衡数据,您没有热点分区,并且您正在分散数据和查询处理负载在集群中任意数量的节点上。缺点是您将数据和查询处理负载分散到集群中任意数量的节点上。 ;-)

    如果您将查询作为 Hadoop/Spark 作业进行,这种方法特别有用,并且它允许完全灵活的时间分辨率。

    【讨论】:

      猜你喜欢
      • 2013-08-02
      • 1970-01-01
      • 2013-04-17
      • 2013-07-13
      • 2014-11-18
      • 1970-01-01
      • 2016-08-29
      • 2011-01-13
      相关资源
      最近更新 更多