计算结果的物化视图

Posted

技术标签:

【中文标题】计算结果的物化视图【英文标题】:Materialized view for calculated results 【发布时间】:2020-08-21 10:08:01 【问题描述】:

我有一个如下表,其中状态是一组有限的更新(例如开始、结束):

CREATE TABLE event_updates (
  event_id Int32,
  timestamp DateTime,
  state String
) ENGINE Log;

我希望能够快速运行如下查询:

SELECT count(*)
FROM (
 SELECT event_id,
  minOrNullIf(timestamp, state = 'Start') as start,
  minOrNullIf(timestamp, state = 'End') as end,
  end - start as duration,
  duration < 10 as is_fast,
  duration > 300 as is_slow
 FROM event_updates
 GROUP BY event_id)
WHERE start >= '2020-08-20 00:00:00'
AND start < '2020-08-20 00:00:00'
AND is_slow;

但是当有大量数据时,这些查询会很慢,我猜是因为每一行都需要计算。

示例数据:

┌─event_id─┬───────────timestamp─┬─state─┐
│        1 │ 2020-08-21 09:58:00 │ Start │
│        1 │ 2020-08-21 10:18:00 │ End   │
│        2 │ 2020-08-21 10:23:00 │ Start │
│        2 │ 2020-08-21 10:23:05 │ End   │
│        3 │ 2020-08-21 10:23:00 │ Start │
│        3 │ 2020-08-21 10:24:00 │ End   │
│        3 │ 2020-08-21 11:24:00 │ End   │
│        4 │ 2020-08-21 10:30:00 │ Start │
└──────────┴─────────────────────┴───────┘

示例查询:

SELECT
    event_id,
    minOrNullIf(timestamp, state = 'Start') AS start,
    minOrNullIf(timestamp, state = 'End') AS end,
    end - start AS duration,
    duration < 10 AS is_fast,
    duration > 300 AS is_slow
FROM event_updates
GROUP BY event_id
ORDER BY event_id ASC

┌─event_id─┬───────────────start─┬─────────────────end─┬─duration─┬─is_fast─┬─is_slow─┐
│        1 │ 2020-08-21 09:58:00 │ 2020-08-21 10:18:00 │     1200 │       0 │       1 │
│        2 │ 2020-08-21 10:23:00 │ 2020-08-21 10:23:05 │        5 │       1 │       0 │
│        3 │ 2020-08-21 10:23:00 │ 2020-08-21 10:24:00 │       60 │       0 │       0 │
│        4 │ 2020-08-21 10:30:00 │                ᴺᵁᴸᴸ │     ᴺᵁᴸᴸ │    ᴺᵁᴸᴸ │    ᴺᵁᴸᴸ │
└──────────┴─────────────────────┴─────────────────────┴──────────┴─────────┴─────────┘

我想要生成的是一个预先计算好的表格,例如:

CREATE TABLE event_stats (
  event_id Int32,
  start Nullable(DateTime),
  end Nullable(DateTime),
  duration Nullable(Int32),
  is_fast Nullable(UInt8),
  is_slow Nullable(UInt8)
);

但我不知道如何使用物化视图创建此表或找到更好的方法。

【问题讨论】:

【参考方案1】:

一开始我会

    使用MergeTree-engine 代替 Log 以获得排序键的好处
CREATE TABLE event_updates (
  event_id Int32,
  timestamp DateTime,
  state String
) ENGINE MergeTree
PARTITION BY toYYYYMM(timestamp)
ORDER BY (timestamp, state);
    通过将 WHERE 子句应用于 timestampstate 来约束原始数据集(在您的查询中处理了整个数据集)
SELECT count(*)
FROM (
 SELECT event_id,
  minOrNullIf(timestamp, state = 'Start') as start,
  minOrNullIf(timestamp, state = 'End') as end,
  end - start as duration,
  duration < 10 as is_fast,
  duration > 300 as is_slow
 FROM event_updates
 WHERE timestamp >= '2020-08-20 00:00:00' AND timestamp < '2020-09-20 00:00:00' 
    AND state IN ('Start', 'End')
 GROUP BY event_id
 HAVING start >= '2020-08-20 00:00:00' AND start < '2020-09-20 00:00:00' 
   AND is_slow);

如果这些没有帮助,需要考虑使用AggregatingMergeTree 来操作预先计算的聚合而不是原始数据。

【讨论】:

以上是关于计算结果的物化视图的主要内容,如果未能解决你的问题,请参考以下文章

物化视图

SparkSpark SQL 物化视图技术原理与实践

ClickHouse 高级物化视图

PG 物化视图

物化视图

物化视图