AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 284332
Accepted
Antonio L.
Antonio L.
Asked: 2021-01-29 18:50:42 +0800 CST2021-01-29 18:50:42 +0800 CST 2021-01-29 18:50:42 +0800 CST

A CTE RECURSIVA não usa um ÍNDICE. (A desativação do seqscan o força a usar um índice e é mais rápido)

  • 772

Suponha as seguintes relações:

  • match(match_id)
  • event(match_id, seq, gt, ...)

Existem os seguintes índices:

  • match(match_id)
  • event(match_id, seq)

Notas adicionais:

  • gt está aumentando monotonicamente
  • Para uma determinada partida, tenho uma coleção de eventos que acontecem em um horário 'gt' específico
  • tanto a partida quanto o evento são visualizações de tapete.
  • Item da lista

Estou usando o postgresql 13.1

Meu objetivo é criar uma consulta CTE RECURSIVA que calcule o delta entre um evento e o próximo, porém acho isso muito lento. Embora isso possa ser resolvido praticamente com uma auto-junção, não estou interessado nisso, quero descobrir por que meu CTE está lento. Acredito que não deve ser tão lento.

Mais números:

  • número de partidas é 400
  • cada partida tem uma média de 541 eventos

Minha consulta CTE RECURSIVA é a seguinte:


WITH RECURSIVE
    delta_gts AS (
        SELECT m.match_id, 1 AS seq, 0 AS gt, 0 AS delta
        FROM matches m

        UNION

        SELECT dgt.match_id, ev.seq AS seq, ev.gt AS gt, (ev.gt - dgt.gt) AS delta
        FROM delta_gts dgt
        JOIN events ev ON ev.match_id = dgt.match_id AND ev.seq = (dgt.seq + 1)

    )

SELECT * FROM delta_gts g

Outras notas que também tentei adicionando o seguinte (apenas para uma partida):

WHERE g.match_id = 'ita_1672780'

e descubro no plano que não há pushdown de predicado. Eu acho que isso foi implementado no pgsql 13.1

Este é o plano:

QUERY PLAN
CTE Scan on delta_gts g  (cost=160601.44..161032.40 rows=21548 width=76) (actual time=173.940..354185.831 rows=220268 loops=1)
"  Buffers: shared hit=5453034 read=596370, temp read=1340253 written=1581611"
  CTE delta_gts
    ->  Recursive Union  (cost=0.00..160601.44 rows=21548 width=76) (actual time=173.931..353944.926 rows=220268 loops=1)
"          Buffers: shared hit=5453034 read=596370, temp read=1340253 written=1580590"
          ->  Seq Scan on netcastingdocument_matches m  (cost=0.00..10.08 rows=408 width=28) (actual time=173.917..174.265 rows=408 loops=1)
                Buffers: shared hit=6
          ->  Hash Join  (cost=14121.22..16016.04 rows=2114 width=76) (actual time=259.550..305.356 rows=190 loops=1158)
                Hash Cond: ((dgt.match_id = ev.match_id) AND ((dgt.seq + 1) = ev.seq))
"                Buffers: shared hit=5453028 read=596370, temp read=1340253 written=1580590"
                ->  WorkTable Scan on delta_gts dgt  (cost=0.00..81.60 rows=4080 width=72) (actual time=0.005..0.067 rows=190 loops=1158)
                ->  Hash  (cost=8106.89..8106.89 rows=288289 width=24) (actual time=257.949..257.949 rows=288323 loops=1158)
                      Buckets: 65536  Batches: 8  Memory Usage: 2484kB
"                      Buffers: shared hit=5453022 read=596370, temp written=1565616"
                      ->  Seq Scan on netcastingdocument_events ev  (cost=0.00..8106.89 rows=288289 width=24) (actual time=0.016..92.171 rows=288323 loops=1158)
                            Buffers: shared hit=5453022 read=596370
Planning:
  Buffers: shared hit=107
Planning Time: 50.290 ms
JIT:
  Functions: 13
"  Options: Inlining false, Optimization false, Expressions true, Deforming true"
"  Timing: Generation 4.108 ms, Inlining 0.000 ms, Optimization 19.158 ms, Emission 154.531 ms, Total 177.796 ms"
Execution Time: 355489.930 ms

Considerações:

  • Ele não está usando o índice (match_id, seq) na tabela de eventos quando a parte recursiva do CTE é executada.
  • Desabilitar o seqscan faz o truque, pois usará o índice para eventos.

Após alguma investigação, parece que o problema é que um SeqScan está sendo executado para procurar o próximo evento que não está correto na minha situação.

postgresql postgresql-13
  • 2 2 respostas
  • 479 Views

2 respostas

  • Voted
  1. Best Answer
    Laurenz Albe
    2021-01-30T03:45:53+08:002021-01-30T03:45:53+08:00

    Pode haver várias causas; Não posso ter certeza, porque você não postou a EXPLAIN (ANALYZE, BUFFERS)saída para ambas as execuções.

    • O PostgreSQL pode estimar incorretamente as contagens de linhas. Executar ANALYZEcomo você fez é uma boa abordagem aqui, mas em uma CTE recursiva as contagens de linhas geralmente são difíceis de prever e é difícil corrigir essas estimativas.

      Se você não se importa com um truque desagradável, você pode tentar adicionar outra condição de junção supérflua para fazer o PostgreSQL pensar que o resultado terá menos linhas:

      JOIN events ev
         ON ev.match_id = dgt.match_id
            AND ev.seq = dgt.seq + 1
            AND ev.seq - 1 = dgt.seq
      
    • O PostgreSQL pode precificar uma varredura de índice muito alta, o que o induz a escolher uma varredura sequencial e uma junção de hash em vez de uma junção de loop aninhado.

      • Se você tiver um SSD como disco, deve diminuir random_page_costpara 1 ou 1.1 para dar ao otimizador do PostgreSQL uma ideia de que as varreduras de índice não são quatro vezes mais caras que as varreduras sequenciais.

      • Se você tiver RAM suficiente, deve definir effective_cache_sizeum valor alto o suficiente para que o PostgreSQL saiba que os dados provavelmente estão armazenados em cache. Isso também reduzirá o custo de uma verificação de índice.

    • 2
  2. bobflux
    2021-02-02T06:00:59+08:002021-02-02T06:00:59+08:00

    calcular o delta entre um evento e o próximo

    por que não usar a função de janela LEAD() ou LAG() que fará o que você deseja. E se puder obter a ordem das linhas de um índice, não precisará fazer nenhuma classificação.

    BEGIN;
    CREATE TABLE events( match_id INTEGER NOT NULL, seq INTEGER NOT NULL, value FLOAT );
    INSERT INTO events SELECT n/500, n%500, random() FROM generate_series(1,500*500) n;
    ALTER TABLE events ADD PRIMARY KEY (match_id, seq);
    CREATE TABLE matches( match_id INTEGER PRIMARY KEY );
    INSERT INTO matches SELECT DISTINCT match_id FROM events;
    COMMIT;
    VACUUM ANALYZE matches, events;
    
    EXPLAIN ANALYZE SELECT match_id, seq, value, 
        lag(value,1,0::FLOAT) OVER (PARTITION BY match_id ORDER BY seq) 
        FROM events;
    
     WindowAgg  (cost=0.42..14009.61 rows=250000 width=24) (actual time=0.037..371.799 rows=250000 loops=1)
       ->  Index Scan using events_pkey on events  (cost=0.42..9634.61 rows=250000 width=16) (actual time=0.024..98.620 rows=250000 loops=1)
     Planning Time: 0.090 ms
     Execution Time: 390.870 ms
    

    Se você quiser a diferença entre uma linha e a anterior, use "value-lag(value,1)" ; também lag() recebe um parâmetro padrão, então se você quiser que o primeiro seja 0 em vez de NULL, use lag(value,1,0::FLOAT). Não parece funcionar se o tipo não for explicitamente convertido.

    Agora, a pergunta inicial...

    WITH RECURSIVE
        delta_gts AS (
            SELECT m.match_id, 1 AS seq, 0::FLOAT AS value, 0::FLOAT AS delta FROM matches m
            UNION ALL
            SELECT dgt.match_id, ev.seq AS seq, ev.value, (ev.value - dgt.value) AS delta
            FROM delta_gts dgt
            JOIN events ev ON ev.match_id = dgt.match_id AND ev.seq = (dgt.seq + 1)
        )
    SELECT * FROM delta_gts g;
    

    UNION remove linhas duplicadas. Como não haverá linhas duplicadas, pois as novas linhas adicionadas por cada consulta recursiva são diferentes das anteriores, isso é um desperdício de CPU, então eu a substituí por UNION ALL, que não faz o trabalho extra. Isso o torna cerca de 2x mais rápido.

     CTE Scan on delta_gts g  (cost=78436.57..79448.59 rows=50601 width=24) (actual time=0.019..715.390 rows=249501 loops=1)
       CTE delta_gts
         ->  Recursive Union  (cost=0.00..78436.57 rows=50601 width=24) (actual time=0.016..437.205 rows=249501 loops=1)
               ->  Seq Scan on matches m  (cost=0.00..8.01 rows=501 width=24) (actual time=0.014..0.133 rows=501 loops=1)
               ->  Hash Join  (cost=7602.00..7741.65 rows=5010 width=24) (actual time=0.294..0.733 rows=499 loops=499)
                     Hash Cond: ((dgt.match_id = ev.match_id) AND ((dgt.seq + 1) = ev.seq))
                     ->  WorkTable Scan on delta_gts dgt  (cost=0.00..100.20 rows=5010 width=16) (actual time=0.000..0.061 rows=500 loops=499)
                     ->  Hash  (cost=3852.00..3852.00 rows=250000 width=16) (actual time=145.065..145.066 rows=250000 loops=1)
                           Buckets: 262144  Batches: 1  Memory Usage: 14744kB
                           ->  Seq Scan on events ev  (cost=0.00..3852.00 rows=250000 width=16) (actual time=0.012..59.861 rows=250000 loops=1)
     Planning Time: 0.278 ms
     Execution Time: 745.422 ms
    

    Com UNION e UNION ALL, eu tenho praticamente o mesmo plano que o seu, exceto que é muito mais rápido. Então, mesma consulta, mesmo plano, velocidade diferente, isso é estranho.

    A grande diferença são seus usos de hash: Lotes: 8 Uso de memória: 2484kB

    E o meu usa apenas um lote. Então eu configurei work_mem para 2 MB (estava em 64 MB) e também obtive um hash multi-lote, que era tão lento quanto sua consulta.

    Parece que, quando o hash pode ser feito em um lote, o postgres o fará apenas uma vez para toda a consulta, mas se tiver que ser feito em vários lotes, ele o refaz para cada iteração da consulta recursiva, ou seja cerca de 500 vezes. Isso deve explicar por que é lento. Aparentemente, o planejador não está ciente disso, então ele escolhe o hashjoin.

    Usar set enable_hashjoin to 'f';antes da consulta faz com que ela use o índice. Isso é muito mais lento do que um hash de 1 bucket feito uma vez e muito mais rápido do que um hash refeito para cada iteração.

    Mas realmente, a solução adequada é usar uma função de janela. Ele também irá lidar adequadamente com uma condição como WHERE match_id=...

    • 0

relate perguntas

  • Posso ativar o PITR depois que o banco de dados foi usado

  • Práticas recomendadas para executar a replicação atrasada do deslocamento de tempo

  • Os procedimentos armazenados impedem a injeção de SQL?

  • Sequências Biológicas do UniProt no PostgreSQL

  • Qual é a diferença entre a replicação do PostgreSQL 9.0 e o Slony-I?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve