1

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

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

不知道如何进行。

4

2 回答 2

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 使用数字数据类型或时间戳,以便进行数字比较而不是字符串比较。

于 2013-06-13T09:43:03.297 回答
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 of your selection 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 作业进行,这种方法特别有用,并且它允许完全灵活的时间分辨率。

于 2014-08-23T07:40:21.940 回答