首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >缓慢Postgres聚合查询

问缓慢Postgres聚合查询
EN

Database Administration用户
提问于 2019-09-28 15:56:56
回答 1查看 679关注 0票数 2

我需要计算一系列测量数据的值。度量数据被划分为1000组连续行,因为查询的每个调用都必须输出一系列1000个值。典型的输入集将包含10,000至1,000,000行。以下是我能想到的最好的解决方案。它工作得很好,但是执行相当慢。因为我的要求之一是经常触发计算,所以我需要为执行时间进行优化。不幸的是,不能预先计算单个组的值,因为每个新的度量行都会影响组大小。

模式设置

代码语言:javascript
复制
create table devices
(
    id varchar not null
        constraint devices_pkey
            primary key
);


create table processes
(
    id        integer not null,

    constraint processes_pkey
        primary key (id, device_id),

    device_id varchar not null
        constraint fk_processes_devices
            references devices
            on delete cascade
);

create index processes_device_id_idx on processes (device_id);


create table measurements
(
    timestamp  timestamp with time zone not null,
    current    real                     not null,
    process_id integer                  not null,
    device_id  varchar                  not null,

    constraint measurements_pkey
        primary key (timestamp, process_id, device_id),

    constraint fk_measurements_processes
        foreign key (process_id, device_id) references processes
            on delete cascade
);

create index measurements_process_id_device_id_idx on measurements (device_id, process_id);


INSERT INTO devices (id) VALUES ('123');

INSERT INTO processes (id, device_id) VALUES (456, '123');

WITH numbers AS (
  SELECT *
  FROM generate_series(1, 1000000)
)

INSERT INTO measurements (timestamp, current, process_id, device_id)
SELECT NOW() + (generate_series * interval '1 second'), generate_series * random(), 456, '123'
FROM numbers;

查询

代码语言:javascript
复制
select min(timestamp) as timestamp,
       case when sum(current) < 0 then -SQRT(AVG(POWER(current, 2))) else SQRT(AVG(POWER(current, 2))) end,
       456            as process_id,
       '123'          as device_id,
       index / 1000   as group_index
from (select timestamp,
             current,
             row_number() over (order by timestamp) as index
      from measurements
      where device_id = '123'
        and process_id = 456
      order by timestamp) as subquery
group by group_index
order by group_index;

数据库-小提琴:https://www.db-fiddle.com/f/uVTcf9Q2JDEkPf3S5hgvfB/2

查询计划可视化:http://tatiyants.com/pev/#/plans/plan_1569689658707

如何优化查询?

查询计划

代码语言:javascript
复制
Sort  (cost=100157.88..100158.38 rows=200 width=60) (actual time=927.340..927.402 rows=1001 loops=1)
  Sort Key: ((subquery.index / 1000))
  Sort Method: quicksort  Memory: 103kB
  ->  HashAggregate  (cost=100144.74..100150.24 rows=200 width=60) (actual time=926.828..927.036 rows=1001 loops=1)
        Group Key: (subquery.index / 1000)
        ->  Subquery Scan on subquery  (cost=0.42..77644.74 rows=1000000 width=20) (actual time=0.049..704.478 rows=1000000 loops=1)
              ->  WindowAgg  (cost=0.42..65144.74 rows=1000000 width=20) (actual time=0.046..576.692 rows=1000000 loops=1)
                    ->  Index Scan using measurements_pkey on measurements  (cost=0.42..50144.74 rows=1000000 width=12) (actual time=0.029..219.951 rows=1000000 loops=1)
                          Index Cond: ((process_id = 456) AND ((device_id)::text = '123'::text))
Planning Time: 0.378 ms
Execution Time: 927.591 ms
EN

回答 1

Database Administration用户

回答已采纳

发布于 2019-09-28 18:46:28

这个计划没有明显的问题,所以没有明显的大的优化要做。从根本上说,你做了很多工作,这需要很多时间。

我可能会先退一步,看看你的商业案例。你为什么需要这个输出?你能“需要”一些更容易优化的东西吗?比如,一块块完整地滑过,而不是一排一排地在遗忘的过程中慢慢地从一大块滑到另一片?或者分区是由可预测的时间戳片定义的,而不是每个分区有1000行?

你没有给出你的版本,但你的小提琴使用9.5。如果我使用最新的版本,并打开JIT (即时编译),我得到了约15%的改善比JIT关闭。

如果我构建了一个只适用于索引扫描的索引,那么我不会得到太大的改进,它有时更快,有时更慢(它还与JIT交互)。但是,如果不是所有的东西都在记忆中,它可能会带来巨大的进步:

代码语言:javascript
复制
create index  on measurements (device_id, process_id, timestamp, current);

如果您正在寻找更复杂的解决方案,您可以编写一个进程,该进程将连接到数据库并侦听新的插入,然后通过使用增量计算将行从一个分区滑动到下一个分区来更新数据,而不是从头开始重新计算每个分区。这类似于移动骨料的工作方式,只不过它只在语句中的框架之间工作,而不是在您需要的语句之间工作。这个应该很快。要获得严格的准确性,这将取决于每个插入的行按照它们的时间戳列的相同顺序提交--这一要求在一般情况下很难保证,但对于您的确切情况可能很容易。

票数 2
EN
页面原文内容由Database Administration提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://dba.stackexchange.com/questions/249853

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档