postgresql 如何提高查询时间序列的性能?

u0sqgete  于 2023-10-18  发布在  PostgreSQL
关注(0)|答案(2)|浏览(141)

我使用以下查询以8小时分段显示机器的性能(尽管它可以更改为1小时、1周、1个月等)。我的表有200万条记录,查询需要10秒执行。这是一个合理的速度,还是可以改进?
我有

  • 8 GB内存
  • Intel Haswell CPU 4核心
  • PostgreSQL 14.2
  • TimescaleDB 2.6.1
  • shared_buffers = 1024MB
  • temp_buffers = 16MB
  • work_css = 64MB

tbl_pieza表是一个超表。

create table tbl_pieza
(
    id_nu_pieza             integer                             not null,
    id_nu_orden_fabricacion integer,
    id_nu_referencia        integer,
    id_nu_operacion         integer,
    id_nu_maquina           integer,
    id_nu_usuario           integer,
    ind_paro                integer,
    ind_validada            integer   default 0,
    nu_segundos             integer,
    dtm_inicio_at           timestamp default CURRENT_TIMESTAMP not null,
    dtm_fin_at              timestamp,
    ind_estatus             integer   default 1,
    dtm_create_at           timestamp,
    dtm_update_at           timestamp default CURRENT_TIMESTAMP,
    ind_retrabajo           integer   default 0,
    primary key (id_nu_pieza, dtm_inicio_at)
);

create index tbl_pieza_dtm_inicio_at_idx
    on tbl_pieza (dtm_inicio_at desc);

create index idx_time_range
    on tbl_pieza (dtm_inicio_at, dtm_fin_at);

WITH Rangos AS (
    SELECT
        generate_series(
            '2023-05-22 16:23:14'::timestamp,
            '2023-05-26 08:23:14'::timestamp,
            '8 hour'::interval
        ) AS inicio,
        generate_series(
            '2023-05-23 00:23:14'::timestamp,
            '2023-05-26 16:23:14'::timestamp,
            '8 hour'::interval
        ) AS fin
),
PiezasPorIntervalo AS (
    SELECT
        r.inicio,
        r.fin,
        p.id_nu_operacion,
        p.id_nu_maquina,
        SUM(
            CASE
                WHEN EXTRACT(epoch FROM p.dtm_fin_at - p.dtm_inicio_at) = 0 THEN 0
                ELSE GREATEST(0, EXTRACT(epoch FROM LEAST(r.fin, p.dtm_fin_at) - GREATEST(r.inicio, p.dtm_inicio_at)) / EXTRACT(epoch FROM p.dtm_fin_at - p.dtm_inicio_at))
            END
        ) as PiezasReales
    FROM Rangos r
    JOIN tbl_pieza p ON p.dtm_inicio_at < r.fin AND p.dtm_fin_at > r.inicio
                            AND p.id_nu_usuario in (1,8,11,43,44,45,46,47,48,49)
                            AND p.id_nu_operacion in (84,85,86,87,88,89,90,91,92,93,118,119)
                            AND p.id_nu_referencia in (46,58,59,60)
                            AND p.id_nu_maquina in (1,2,3,8)
    GROUP BY r.inicio, r.fin, p.id_nu_operacion, p.id_nu_maquina
)
SELECT
    p.inicio as fecha_inicio,
    p.fin as fecha_fin,
    p.id_nu_maquina as id_maquina,
    CASE
        WHEN o.ciclo_estimado + o.tiempo_cambio_estimado = 0 THEN 0
        ELSE (p.PiezasReales::decimal / (28800 / (o.ciclo_estimado + o.tiempo_cambio_estimado))) * 100
    END as resultado
FROM PiezasPorIntervalo p
JOIN operacion o ON o.id_operacion = p.id_nu_operacion
ORDER BY fecha_inicio;

我在一个系统上运行这个与上述规格。任何关于如何优化此查询以获得更好性能的建议都将非常感谢。谢谢你,谢谢!
EXPLAIN输出(ANALYZE、BUFFERS)
Explain output
PiezasPorIntervalo是需要最长时间的部分,我会马上解释我做什么
假设我们有一个生产表,其中包含以下条目:

Piece | Production Start         | Production End
----- | ------------------------| -------------------------
A     | 2023-05-23 08:00:00     | 2023-05-23 10:00:00
B     | 2023-05-23 09:30:00     | 2023-05-23 12:00:00
C     | 2023-05-23 10:30:00     | 2023-05-23 11:30:00
D     | 2023-05-23 12:00:00     | 2023-05-23 13:30:00

假设我们要计算从“2023-05-23 09:00:00”到“2023 - 05 -23 11:00:00”的特定时间间隔内的“PiezasReales”的数量。以下是逐步计算:
时间范围和单件生产时间之间的交叉持续时间:

For Piece A: MIN(2023-05-23 11:00:00, 2023-05-23 10:00:00) - MAX(2023-05-23 09:00:00, 2023-05-23 08:00:00) = 1 hour
For Piece B: MIN(2023-05-23 11:00:00, 2023-05-23 12:00:00) - MAX(2023-05-23 09:00:00, 2023-05-23 09:30:00) = 0.5 hours
For Piece C: MIN(2023-05-23 11:00:00, 2023-05-23 11:30:00) - MAX(2023-05-23 09:00:00, 2023-05-23 10:30:00) = 0.5 hours
For Piece D: No intersection with the interval, so the duration is 0.

作品总时长:

For Piece A: 2023-05-23 10:00:00 - 2023-05-23 08:00:00 = 2 hours
For Piece B: 2023-05-23 12:00:00 - 2023-05-23 09:30:00 = 2.5 hours
For Piece C: 2023-05-23 11:30:00 - 2023-05-23 10:30:00 = 1 hour
For Piece D: 2023-05-23 13:30:00 - 2023-05-23 12:00:00 = 1.5 hours

时间间隔中的时间分数:

For Piece A: 1 hour / 2 hours = 0.5
For Piece B: 0.5 hours / 2.5 hours = 0.2
For Piece C: 0.5 hours / 1 hour = 0.5
For Piece D: 0 (since there's no intersection with the interval).

区间内的PiezasReales数:
时间间隔内的时间分数之和:0.5 + 0.2 + 0.5 + 0 = 1.2因此,在这个特定的时间间隔内,有相当于1.2件的“RealPieces”,考虑到每个零件在该时间间隔内生产的时间比例。

rdrgkggo

rdrgkggo1#

您所有的时间(实际上,比您所有的时间更多,我不知道这是如何发生的)似乎都要用于JIT编译。你可能不会从中受益。关闭JIT。我认为开发人员默认开启JIT是一个错误,因为它似乎伤害的人比帮助的人更多。所以把它关了就行了。
如果仍然太慢,发布一个新计划。JIT的影响往往会出现在毫无意义的随机位置,使得当前的JIT污染计划难以解释。

ma8fv8wu

ma8fv8wu2#

首先要做的是创建一个表格时间表,其中有一个唯一的列作为PK,以替换查询中的generate series语句:

CREATE TABLE TIME_SERIES AS
SELECT *
FROM generate_series(
            '2023-05-22 16:23:14'::timestamp,
            '2023-05-26 08:23:14'::timestamp,
            '8 hour'::interval
        ) AS d UNION 
SELECT * 
FROM generate_series(
            '2023-05-23 00:23:14'::timestamp,
            '2023-05-26 16:23:14'::timestamp,
            '8 hour'::interval
        ) AS f;

第二件要做的事情是用一系列带有唯一列(即PK)的临时表替换IN操作符
最后要做的是使用INCLUDE索引而不是经典索引来拥有覆盖索引。

相关问题