Cassandra时间序列数据模型与计数器列

时间:2013-06-12 21:41:52

标签: cassandra data-modeling cql3

我正在尝试为时间序列建模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';

但是我现在想要在'us'='username'的范围内取回'bytes'的值:

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

我正在收回错误:

  

错误请求:随机分区程序的分区键仅支持EQ和IN关系(除非您使用令牌()   功能)

我假设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';`

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

不确定如何继续。

2 个答案:

答案 0 :(得分:4)

如果需要复合分区键,则需要额外的括号:

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,以便进行数字比较而不是字符串比较。

答案 1 :(得分:2)

我认为你真的不需要担心行大小。你可以将一个分区增长到20亿个单元,所以你可能没问题。

如果你真的希望数据分发不好,只需插入一个随机填充的枚举。

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

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

然后,要更新消费信息,您可能会执行以下操作:     您选择的#language     ts = now()     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作业进行查询,这种方法特别有用,并且可以实现完全灵活的时间分辨率。