Clickhouse 在所有表上选择最后一条没有 max() 的记录

Art*_*sev 4 sql clickhouse

我的表中有数十亿行 4k 可变参数,我需要获取其中 500 个参数的最后一个值 我的表按天分区并按参数 id 排序,所以我只需要找到具有所需 id 的最后一条记录

SELECT max(time)
FROM obj_ntgres.param_values_history
PREWHERE param_id = 4171
Run Code Online (Sandbox Code Playgroud)

工作慢:经过:0.437 秒。处理 256 万行,5.21 MB(587 万行/秒,11.92 MB/秒)

SELECT *
FROM obj_ntgres.param_values_history
PREWHERE param_id = 4171
ORDER BY time DESC
LIMIT 1
Run Code Online (Sandbox Code Playgroud)

较慢:集合中有 1 行。经过:3.413 秒。处理 256 万行,5.45 MB(75121 万行/秒,1.60 MB/秒)

桌子

CREATE TABLE obj_ntgres.param_values_history (

  time DateTime,

  param_id UInt16,

  param_value Float32,

  param_value_quality Decimal(1, 0),

  msec Decimal(3, 0)

) ENGINE = MergeTree PARTITION BY toStartOfDay(time)

ORDER BY

  param_id SETTINGS index_granularity = 8192
Run Code Online (Sandbox Code Playgroud)

也许您有一些想法如何让它更快?

我的意思是:在所有表上不使用 max() 找到最后一个元素

fil*_*nov 8

实际上,它仍然需要使用相同的方法扫描相当多的数据param_id

可能的方法很少。在所有情况下,一开始您都需要将time列添加到表排序键:

    CREATE TABLE param_values_history (
      time DateTime,
      param_id UInt16,
      param_value Float32,
      param_value_quality Decimal(1, 0),
      msec Decimal(3, 0)
    ) ENGINE = MergeTree PARTITION BY toStartOfDay(time)
    ORDER BY
      (param_id,time) SETTINGS index_granularity = 8192
Run Code Online (Sandbox Code Playgroud)

之后 - 如果您的数据是时间对齐的,即如果您确切地知道所有 500 个参数在过去几秒/分钟内都有一些值,您可以添加一个像 之类的过滤器,它AND time > now() - INTERVAL 10 MINUTES会工作得非常快(不需要扫描很多行)。

如果您的某些参数没有定期活动,情况会更糟。

在这种情况下,最快的方法是通过物化视图缓存每个参数的最后一次,甚至整个最后一行。像这样的东西:

CREATE MATERIALIZED VIEW last_positions
 Engine=ReplacingMergeTree(max_time)
ORDER BY param_id
PARTITION BY tuple()
AS SELECT param_id, max(time) as max_time
FROM param_values_history
GROUP BY param_id;

SELECT * FROM param_values_history PREWHERE (param_id,time) IN (SELECT param_id, max(max_time) FROM last_positions GROUP BY param_id);
Run Code Online (Sandbox Code Playgroud)

或者:MV中最后一行全部收集

CREATE MATERIALIZED VIEW last_positions
 Engine=ReplacingMergeTree(max_time)
ORDER BY param_id
PARTITION BY tuple()
AS SELECT param_id,
   argMax(param_value, time) as _param_value, 
   argMax(param_value_quality, time) as _param_value_quality, 
   argMax(param_value, msec) as _msec, 
   max(time) as max_time
FROM param_values_history
GROUP BY param_id;

SELECT * FROM last_positions FINAL;
Run Code Online (Sandbox Code Playgroud)