【问题标题】:Cassandra grouping with filter带过滤器的 Cassandra 分组
【发布时间】:2017-05-19 09:16:55
【问题描述】:

我有一张每分钟完成的事件表。我希望能够按时间段过滤这些事件,并汇总小时/天/等的数据。

我的数据模型:

create table min_dev_data (
device TEXT,
event_time BIGINT,
hour BIGINT,
day BIGINT,
value DOUBLE,
PRIMARY KEY ((device), event_time)
)

CREATE MATERIALIZED VIEW hour_dev_data AS
SELECT device, event_time, hour, value
FROM min_dev_data
WHERE hour IS NOT NULL AND value IS NOT NULL 
      and event_time IS NOT NULL AND device IS NOT NULL
PRIMARY KEY ((device), hour, event_time)

我的查询是

select hour, sum(value) 
from hour_dev_data 
where device = 'tst' and event_time < 149000000 group by device, hour;

因错误而失败 code=2200 [无效查询] message="PRIMARY KEY 列 "event_time" 不能被限制,因为前列 "hour" 不受限制"

使其工作的唯一方法是添加 ALLOW FILTERING,这是不可预测的。

如何更改我的数据模型以解决我的查询并避免 ALLOW FILTERING 模式?

【问题讨论】:

  • 小时是否在 0-23 范围内?或者它是一个小时开始的unix时间?
  • 是unix时间戳。小时是小时的结束 - 像小时 = event_time + (3600 - event_time % 3600) 计算它
  • 您要回答的问题是什么?它看起来像“这些时间之间特定device 按小时分组的value 的总和”。我不确定您要查询的确切时间范围是多少。是某个特定小时,还是某个时间段内的某个小时范围,还是某个时间之后的所有小时?
  • 要回答的问题 - “显示特定设备 (device = 'tst') 在特定时间段内 (event_time 介于 1 和 149000000 之间) 的汇总汇总值 (sum(value)) 并具有指定的粒度(分钟/小时/天)”

标签: cassandra


【解决方案1】:

您必须主动产生这些结果:

create table min_dev_data (
    device TEXT,
    event_time BIGINT,
    hour BIGINT,
    day BIGINT,
    value DOUBLE,
    PRIMARY KEY ((device), event_time)
) WITH CLUSTERING ORDER BY (event_time DESC);

create table hour_dev_data (
    device TEXT,
    hour BIGINT,
    day BIGINT,
    event_time BIGINT,
    value DOUBLE,
    PRIMARY KEY ((device), event_time)
) WITH CLUSTERING ORDER BY (event_time DESC);

create table day_dev_data (
    device TEXT,
    day BIGINT,
    event_time BIGINT,
    value DOUBLE,
    PRIMARY KEY ((device), event_time)
) WITH CLUSTERING ORDER BY (event_time DESC);

每个表只满足一个粒度。

您每小时查询分钟数据以获取每台设备的最新小时数据,例如:

SELECT * FROM min_dev_data WHERE device = X AND event_time < YYYY

在应用程序级别求和并将此值存储到小时表中:

INSERT INTO hour_dev_data (device, hour, day, event_time, value) VALUES (....);

您每天都查询小时表以生成进一步的聚合数据:

SELECT * FROM hour_dev_data WHERE device = X AND event_time < YYYY

在应用程序级别求和并将此值存储到日表中。

请考虑添加某种形式的分桶,因为每隔一分钟,您的分钟表将在两个月内拥有宽分区。如果您将表格保持在相反的顺序(就像我所做的那样)并且只查询最后几个小时,这应该不是问题。但是,如果您还想及时查询,那么您肯定必须在表中使用分桶。

【讨论】:

  • 谢谢,对我来说很有意义,但对于来自 RDBMS 世界的人来说,这有点不明显 :) 还有一个问题 - 你提到数据应该在应用层聚合 - 它是不是常见的做法,我应该避免使用 cassandra 分组和聚合函数吗?
  • @sev3ryn 是的,您可以使用 cassandra 执行此类任务,但您应该关注性能:聚合查询需要从磁盘读取一些数据,如果数据难以检索(例如非常宽分区)您的查询将执行不佳。我在这里使用的技巧是在数据仍然易于读取时读取数据(例如,在分区的开头)。一旦你开始添加越来越多的数据,并且仍然需要访问旧数据,可能会出现一些问题,这就是为什么我建议在这种情况下进行分桶。
【解决方案2】:

我认为你已经做对了,但你需要将 event_time 上的过滤器更改为 hour 上的过滤器。

select hour, sum(value)  
from hour_dev_data  
where device = 'tst' and hour < 1500000000 
group by device, hour;

当您在 event_time 上进行过滤时,您隐含地需要对行进行全面扫描,因为 event_time 在一小时后聚集。要按event_time 过滤,需要检查每个单元格以检查event_time。当你按hour过滤时,它在聚类键中排在第一位,因此可以有效地扫描和过滤。有关更多信息,请参阅ALLOW FILTERING 上的此帖子。

我同意 xmas79 的观点,即您可能希望在某个级别上进行分组,可能按月或按年进行,具体取决于您的活动频率。如果您总是要查找最新的值,那么将集群键顺序设置为 desc 也可能会有所帮助:

CREATE MATERIALIZED VIEW hour_dev_data3 AS
SELECT device, event_time, hour, value
FROM min_dev_data
WHERE hour IS NOT NULL AND value IS NOT NULL 
      and event_time IS NOT NULL AND device IS NOT NULL
PRIMARY KEY ((device), hour, event_time)
WITH CLUSTERING ORDER BY (hour DESC);

像 xmas79 建议的调度聚合会更有效,因为求和是一次完成,而不是每次读取完成时求和,但是它确实增加了更多的维护负担,具体化视图会为您处理。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-10-19
    • 1970-01-01
    • 2018-08-09
    • 2018-06-02
    • 2018-07-10
    • 1970-01-01
    • 1970-01-01
    • 2020-01-17
    相关资源
    最近更新 更多