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 / 341514
Accepted
Parker
Parker
Asked: 2024-08-08 21:50:13 +0800 CST2024-08-08 21:50:13 +0800 CST 2024-08-08 21:50:13 +0800 CST

Qual é a causa dessa consulta lenta intermitente ao contar linhas em uma única coluna de texto indexada no PostgreSQL?

  • 772

Tenho uma tabela com 2.395.015 linhas, onde uma TEXTcoluna tem um de três valores e nunca é NULL. Tenho problemas intermitentes de desempenho de consulta ao contar o número de linhas em que o valor corresponde à maioria (>99%) das linhas. Quero corrigir esse problema de desempenho. Essas consultas devem retornar contagens exatas, então não posso usar contagens aproximadas.

corpus=# \d metadata
                             Table "public.metadata"
    Column     |            Type             | Collation | Nullable |    Default
---------------+-----------------------------+-----------+----------+----------------
 id            | text                        |           | not null |
 priority      | integer                     |           | not null | 10
 media_type    | text                        |           | not null |
 modified      | timestamp without time zone |           | not null | now()
 processed     | timestamp without time zone |           |          |
 status        | text                        |           | not null | 'QUEUED'::text
 note          | text                        |           |          |
 content       | text                        |           |          |
 resolved      | text                        |           |          |
 response_time | integer                     |           |          |
 luid          | integer                     |           | not null |
 jamo_date     | timestamp without time zone |           |          |
 audit_path    | text                        |           |          |
Indexes:
    "metadata_pkey" PRIMARY KEY, btree (id)
    "metadata_id_idx" btree (id)
    "metadata_luid_idx" btree (luid)
    "metadata_modified_idx" btree (modified DESC)
    "metadata_processed_idx" btree (processed DESC)
    "metadata_status_idx" btree (status)
Check constraints:
    "media_type_ck" CHECK (media_type = ANY (ARRAY['text/json'::text, 'text/yaml'::text]))
    "status_ck" CHECK (status = ANY (ARRAY['QUEUED'::text, 'PROCESSED'::text, 'ERROR'::text]))
Foreign-key constraints:
    "metadata_luid_fkey" FOREIGN KEY (luid) REFERENCES concept(luid) ON DELETE CASCADE

corpus=#

Tenho algumas consultas simples que contam o número de linhas que correspondem a um dos três códigos de status ( QUEUED, PROCESSED, ERROR). Há 0 linhas que correspondem a QUEUED, 9.794 que correspondem a ERROR, e 2.385.221 que correspondem a PROCESSED. Quando executo uma consulta idêntica em cada um desses códigos de status, geralmente obtenho um conjunto de resultados prontamente:

corpus=# EXPLAIN ANALYZE VERBOSE SELECT COUNT(*) FROM metadata WHERE status='QUEUED';
                                                                          QUERY PLAN

---------------------------------------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=1947.17..1947.18 rows=1 width=8) (actual time=2.935..2.936 rows=1 loops=1)
   Output: count(*)
   ->  Index Only Scan using metadata_status_idx on public.metadata  (cost=0.43..1915.97 rows=12480 width=0) (actual time=2.932..2.933 rows=0 loops=1)
         Output: status
         Index Cond: (metadata.status = 'QUEUED'::text)
         Heap Fetches: 0
 Planning Time: 0.734 ms
 Execution Time: 2.988 ms
(8 rows)
corpus=# EXPLAIN ANALYZE VERBOSE SELECT COUNT(*) FROM metadata WHERE status='ERROR';
                                                                             QUERY PLAN

--------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=1184.19..1184.20 rows=1 width=8) (actual time=1484.763..1484.764 rows=1 loops=1)
   Output: count(*)
   ->  Index Only Scan using metadata_status_idx on public.metadata  (cost=0.43..1165.26 rows=7569 width=0) (actual time=4.235..1484.029 rows=9794 loops=1)
         Output: status
         Index Cond: (metadata.status = 'ERROR'::text)
         Heap Fetches: 9584
 Planning Time: 0.072 ms
 Execution Time: 1484.786 ms
(8 rows)

corpus=#
corpus=# EXPLAIN ANALYZE VERBOSE SELECT COUNT(*) FROM metadata WHERE status='PROCESSED';
                                                                                          QUERY PLAN

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Finalize Aggregate  (cost=261398.83..261398.84 rows=1 width=8) (actual time=741.319..749.026 rows=1 loops=1)
   Output: count(*)
   ->  Gather  (cost=261398.62..261398.83 rows=2 width=8) (actual time=741.309..749.020 rows=3 loops=1)
         Output: (PARTIAL count(*))
         Workers Planned: 2
         Workers Launched: 2
         ->  Partial Aggregate  (cost=260398.62..260398.63 rows=1 width=8) (actual time=735.099..735.100 rows=1 loops=3)
               Output: PARTIAL count(*)
               Worker 0: actual time=730.871..730.872 rows=1 loops=1
               Worker 1: actual time=733.435..733.436 rows=1 loops=1
               ->  Parallel Index Only Scan using metadata_status_idx on public.metadata  (cost=0.43..257903.37 rows=998100 width=0) (actual time=0.065..700.529 rows=795074 loops=3)
                     Output: status
                     Index Cond: (metadata.status = 'PROCESSED'::text)
                     Heap Fetches: 747048
                     Worker 0: actual time=0.060..702.980 rows=670975 loops=1
                     Worker 1: actual time=0.076..686.946 rows=1010099 loops=1
 Planning Time: 0.085 ms
 Execution Time: 749.068 ms
(18 rows)

corpus=#

Mas, ocasionalmente, a contagem de PROCESSEDlinhas leva muito tempo (às vezes, vários minutos):

corpus=# EXPLAIN ANALYZE VERBOSE SELECT COUNT(*) FROM metadata WHERE status='PROCESSED';
                                                                                           QUERY PLAN

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Finalize Aggregate  (cost=261398.83..261398.84 rows=1 width=8) (actual time=30019.273..30019.336 rows=1 loops=1)
   Output: count(*)
   ->  Gather  (cost=261398.62..261398.83 rows=2 width=8) (actual time=30019.261..30019.326 rows=3 loops=1)
         Output: (PARTIAL count(*))
         Workers Planned: 2
         Workers Launched: 2
         ->  Partial Aggregate  (cost=260398.62..260398.63 rows=1 width=8) (actual time=29967.734..29967.735 rows=1 loops=3)
               Output: PARTIAL count(*)
               Worker 0: actual time=29939.915..29939.916 rows=1 loops=1
               Worker 1: actual time=29944.395..29944.395 rows=1 loops=1
               ->  Parallel Index Only Scan using metadata_status_idx on public.metadata  (cost=0.43..257903.37 rows=998100 width=0) (actual time=75.385..29931.795 rows=795074 loops=3)
                     Output: status
                     Index Cond: (metadata.status = 'PROCESSED'::text)
                     Heap Fetches: 747151
                     Worker 0: actual time=128.857..29899.156 rows=916461 loops=1
                     Worker 1: actual time=28.609..29905.708 rows=854439 loops=1
 Planning Time: 421.203 ms
 Execution Time: 30019.440 ms
(18 rows)

corpus=#

Enquanto a consulta acima está sendo executada lentamente, consigo consultar a mesma tabela para qualquer um dos outros dois códigos, e essas consultas retornam em 1 segundo. Procurei por bloqueios de tabela (não há nenhum). Isso acontece mesmo quando não há outras consultas ou inserções de tabela em execução.

  • Quais são as possíveis causas para essas consultas lentas intermitentes?
  • Que depuração adicional posso tentar para obter mais informações sobre essas consultas lentas?
  • Há alguma configuração de servidor relevante?
  • Existe uma maneira mais eficiente de indexar/codificar essas colunas (por exemplo, devo usar um CHAR(1)), ou mesmo um SMALLINT? Se sim, qual índice deve ser usado para a coluna?

Se eu usar um CHAR(1), há alguma diferença entre as seguintes restrições:

  • ALTER TABLE jgi_metadata ADD CONSTRAINT status_code_ck CHECK (status_code = ANY (ARRAY['Q'::char(1), 'P'::char(1), 'E'::char(1)]));

  • ALTER TABLE jgi_metadata ADD CONSTRAINT status_code_ck CHECK (status_code IN ('Q', 'P', 'E'));

  • Um índice parcial poderia ser usado para esta coluna, mesmo que isso nunca aconteça NULL?

  • Devo dividir o PROCESSEDoff em uma coluna booleana e então usar a statuscoluna somente para os outros códigos e torná-la anulável com um índice parcial?

Este é o PostgreSQL 11 com configurações padrão, rodando no Linux.

Outras coisas que tentei:

  • Aumentou o work_mem para 100 MB (via postgresql.conf). Nenhuma alteração no desempenho.
  • Tentei criar um índice parcial na coluna de status.

Atualização: Descobri que esse problema de desempenho não tem nada a ver com a coluna de status, mas sim com o tamanho da tabela em si, como mostra a consulta de 2 minutos a seguir:

corpus=# EXPLAIN ANALYZE VERBOSE SELECT COUNT(*) FROM metadata;
                                                                                            QUERY PLAN

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Finalize Aggregate  (cost=196398.52..196398.53 rows=1 width=8) (actual time=118527.897..118554.762 rows=1 loops=1)
   Output: count(*)
   ->  Gather  (cost=196398.30..196398.51 rows=2 width=8) (actual time=118522.165..118554.756 rows=3 loops=1)
         Output: (PARTIAL count(*))
         Workers Planned: 2
         Workers Launched: 2
         ->  Partial Aggregate  (cost=195398.30..195398.31 rows=1 width=8) (actual time=118491.043..118491.044 rows=1 loops=3)
               Output: PARTIAL count(*)
               Worker 0: actual time=118475.143..118475.144 rows=1 loops=1
               Worker 1: actual time=118476.110..118476.111 rows=1 loops=1
               ->  Parallel Index Only Scan using metadata_status_idx on public.metadata  (cost=0.43..192876.13rows=1008870 width=0) (actual time=71.797..118449.265 rows=809820 loops=3)
                     Output: status
                     Heap Fetches: 552630
                     Worker 0: actual time=75.877..118434.476 rows=761049 loops=1
                     Worker 1: actual time=104.872..118436.647 rows=745770 loops=1
 Planning Time: 592.040 ms
 Execution Time: 118554.839 ms
(17 rows)

corpus=#

Isso parece ser muito semelhante a outras perguntas agora, então estou tentando estratégias de mitigação a partir desta resposta :

  • VACUUM ANALYZE metadata;A primeira vez COUNT(*)que isso levou 5 segundos, as contagens subsequentes levaram 190 ms.

Outros pensamentos:

  • Ajudaria se a coluna de status fosse dividida em sua própria tabela, com uma chave estrangeira na metadatatabela?

Nota: Estou me convencendo de que esta pergunta é uma duplicata de várias outras perguntas aqui:

  • Contagens extremamente lentas do PostgreSQL
  • As consultas count(*) são muito lentas, mesmo com uma varredura somente de índice
  • Por que algumas consultas de contagem são tão lentas?
  • Otimizando o resultado da contagem de seleção no Postgresql
  • https://stackoverflow.com/questions/58449716/postgres-why-does-select-count-take-so-long
  • https://stackoverflow.com/questions/16916633/if-postgresql-count-is-always-slow-how-to-paginate-complex-queries
  • https://stackoverflow.com/questions/7943233/fast-way-to-discover-the-row-count-of-a-table-in-postgresql/7945274#7945274

Esta resposta pode conter a melhor solução para este problema:

  • https://stackoverflow.com/a/7945274/2074605

Conforme solicitado, aqui está uma análise do plano de consulta com buffers:

EXPLAIN (ANALYZE, BUFFERS, VERBOSE) SELECT COUNT(*) FROM metadata;

                                                                                           QUERY PLAN

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Finalize Aggregate  (cost=80771.95..80771.96 rows=1 width=8) (actual time=26711.481..26716.494 rows=1 loops=1)
   Output: count(*)
   Buffers: shared hit=293915 read=19595 dirtied=282 written=12
   ->  Gather  (cost=80771.73..80771.94 rows=2 width=8) (actual time=26711.203..26716.488 rows=3 loops=1)
         Output: (PARTIAL count(*))
         Workers Planned: 2
         Workers Launched: 2
         Buffers: shared hit=293915 read=19595 dirtied=282 written=12
         ->  Partial Aggregate  (cost=79771.73..79771.74 rows=1 width=8) (actual time=26565.622..26565.623 rows=1 loops=3)
               Output: PARTIAL count(*)
               Buffers: shared hit=293915 read=19595 dirtied=282 written=12
               Worker 0: actual time=26530.890..26530.891 rows=1 loops=1
                 Buffers: shared hit=105264 read=6760 dirtied=145 written=5
               Worker 1: actual time=26530.942..26530.942 rows=1 loops=1
                 Buffers: shared hit=84675 read=7529 dirtied=46 written=2
               ->  Parallel Index Only Scan using metadata_status_idx on public.metadata  (cost=0.43..77241.05 rows=1012275 width=0) (actual time=42.254..26529.232 rows=809820 loops=3)
                     Output: status
                     Heap Fetches: 17185
                     Buffers: shared hit=293915 read=19595 dirtied=282 written=12
                     Worker 0: actual time=59.291..26494.376 rows=815113 loops=1
                       Buffers: shared hit=105264 read=6760 dirtied=145 written=5
                     Worker 1: actual time=31.165..26484.729 rows=1036972 loops=1
                       Buffers: shared hit=84675 read=7529 dirtied=46 written=2
 Planning Time: 98.400 ms
 Execution Time: 26716.529 ms
(25 rows)
query-performance
  • 4 4 respostas
  • 107 Views

4 respostas

  • Voted
  1. Stanislav Bashkyrtsev
    2024-08-12T23:55:05+08:002024-08-12T23:55:05+08:00

    Meu palpite é que às vezes muitas das páginas do heap são armazenadas em buffer, e as consultas são executadas rapidamente. Outras vezes, o buffer é carregado com outras páginas, e então você acaba esperando para ler os dados do disco.

    Há dois problemas com esse tipo de consulta:

    1. Mesmo que a consulta seja coberta pelo índice, ela tem que buscar tuplas de heap por causa do MVCC - ela não sabe quais dos registros de índice são visíveis para o tx atual. Isso pode ser melhorado se muitas páginas acabarem no Visibility Map . Mas se você atualizar constantemente registros em páginas antigas, isso não vai durar muito.
    2. Independentemente das otimizações, isso é apenas uma consulta ruim. A cardinalidade da sua coluna é muito baixa. Se a consulta precisar inspecionar quase todos os dados, ela não será rápida em tabelas grandes. Talvez você deva manter o controle das contagens em uma tabela separada e atualizar essas contagens quando as linhas na tabela principal forem modificadas.

    O Postgres mantém estatísticas para as colunas, incluindo os histogramas de quantas linhas de cada valor esperar. Se estatísticas aproximadas forem suficientes para você, você pode simplesmente consultar pg_stats. Outra abordagem pode ser usar amostragem. Também é uma aproximação. Mas se você precisa de números exatos, então você deve considerar fazer algo mais sofisticado e manter as estatísticas em uma tabela separada você mesmo.

    Retrabalhar a arquitetura para armazenar estatísticas pré-calculadas

    Há um limite para o quanto essa arquitetura pode escalar - e não é muito. Toda vez que você executar essa consulta, você terá que fazer uma varredura sequencial da tabela (os índices são inúteis).

    Se esta for uma consulta crítica, você pode reformular sua arquitetura para manter as estatísticas resumidas em uma tabela separada:

    1. Adicionar coluna counted default false,
    2. Em um trabalho em segundo plano, uma vez a cada N minutos, o salvamento é contabilizado em alguma outra tabela:
    with counted as(
      update record_status set counted=true 
      where not counted 
      returning 1
    )
    update stats set counts=counts+(select count(*) from counted)
    

    Então, quando você precisar fazer a contagem, o que resta é somar as estatísticas e as novas contagens que não foram atualizadas pelo trabalho em segundo plano:

    select (select counts from stats)
           +
           (select count(*) from record_status where not counted)
    

    O countedprecisa ser indexado (um índice parcial seria perfeito). Isso dará resultados instantaneamente.

    Se você precisar dar suporte à exclusão também, em vez de countedapenas 2 valores, você pode transformá-lo em 3 valores: JUST_INSERTED, JUST_DELETED, COUNTED. Então, em vez de excluir o registro imediatamente - marque-o como JUST_DELETED, para atualizar as estatísticas no mesmo trabalho em segundo plano. Mas, dessa vez, subtraia as contagens.

    • 1
  2. Parker
    2024-08-12T23:05:00+08:002024-08-12T23:05:00+08:00

    Com base nos meus testes descritos na minha pergunta revisada e nos conselhos das respostas a perguntas semelhantes (veja aqui e aqui ), implementei as seguintes alterações:

    • Foi criado um índice parcial na coluna de status.CREATE INDEX status_not_processed_idx ON metadata (status) WHERE status<>'PROCESSED';
    • Mesa aspirada.VACUUM ANALYZE metadata;
    • Configurações de vácuo automático ajustadas na mesa.ALTER TABLE metadata SET (autovacuum_vacuum_scale_factor = 0, autovacuum_analyze_scale_factor = 0, autovacuum_vacuum_threshold = 10000, autovacuum_analyze_threshold = 10000);
    • Criou uma função fast_row_count(table_name) com base no método descrito aqui :
    CREATE OR REPLACE FUNCTION fast_count_rows(table_name text)
    RETURNS bigint
    AS $$
    SELECT
    (
      CASE WHEN c.reltuples < 0 THEN NULL
           WHEN c.relpages = 0 THEN float8 '0'
           ELSE c.reltuples / c.relpages END * (pg_catalog.pg_relation_size(c.oid) / pg_catalog.current_setting('block_size')::int)
    )::bigint
      FROM pg_catalog.pg_class c
      WHERE c.oid = CONCAT('public.',table_name)::regclass
    $$ LANGUAGE SQL STABLE;
    
    corpus=> SELECT fast_count_rows('metadata');
     fast_count_rows
    -----------------
             2192335
    
    • Evite usar COUNT(*)ou conte com PROCESSEDvalor:
    CREATE OR REPLACE VIEW metadata_queue_statistics AS
    SELECT
      COUNT(*) FILTER (WHERE status = 'QUEUED'::text) AS queued,
      fast_count_rows('metadata') - COUNT(*) FILTER (WHERE status = ANY (ARRAY ['QUEUED'::text, 'ERROR'::text])) AS completed,
      COUNT(*) FILTER (WHERE status = 'ERROR'::text) AS failed,
      fast_count_rows('metadata') AS total
    FROM metadata
    ;
    
    corpus=> SELECT * FROM metadata_queue_statistics;
    queued | failed | completed |  total
    --------+--------+-----------+---------
      19258 |  11372 |   2398829 | 2429459
    (1 row)
    

    O teste de desempenho inicial parecia promissor (422 ms), mas testes subsequentes dessa abordagem produziram o mesmo problema de desempenho:

    corpus=> EXPLAIN (ANALYZE, BUFFERS, VERBOSE) SELECT * FROM metadata_queue_statistics;
    
                            QUERY PLAN
    
    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
     Aggregate  (cost=127854.78..127855.29 rows=1 width=32) (actual time=76853.872..76853.872 rows=1 loops=1)
       Output: count(*) FILTER (WHERE (metadata.status = 'QUEUED'::text)), (fast_count_rows('metadata'::text) - count(*) FILTER (WHERE (metadata.status = ANY ('{QUEUED,ERROR}'::text[])))), count(*) FILTER (WHERE (metadata.status = 'ERROR'::text)), fast_count_rows('metadata'::text)
       Buffers: shared hit=295240 read=26638 dirtied=383 written=18
       ->  Index Only Scan using metadata_status_idx on public.metadata  (cost=0.43..91412.89 rows=2429459 width=9) (actual time=0.071..76432.532 rows=2429459 loops=1)
             Output: metadata.status
             Heap Fetches: 28878
             Buffers: shared hit=295234 read=26638 dirtied=383 written=18
     Planning Time: 124.091 ms
     Execution Time: 76853.911 ms
    (9 rows)
    

    Não vou marcar isso como resposta para minha pergunta porque o problema de desempenho persistiu. Estou deixando isso aqui como um exemplo de uma solução que falhou.

    Também tentou:

    • Aumentou shared_buffersde postgresql.confpadrão 128MBpara 2GB. (nenhuma alteração significativa no desempenho da consulta)
    • 0
  3. Best Answer
    Parker
    2025-02-26T22:53:36+08:002025-02-26T22:53:36+08:00

    Esse problema finalmente chegou a um ponto em que não pude mais ignorá-lo.

    Finalmente resolvi esse problema de desempenho movendo o código de status para sua própria tabela e usando um inteiro luid(identificador exclusivo local de incremento automático) da tabela principal como chave. O código de status em si agora é apenas um único caractere. O banco de dados tem até 4.736.786 registros e a visualização (também modificada, colada abaixo) conclui a consulta usando a nova tabela em menos de um segundo.

    ALTER TABLE jgi_metadata ADD CONSTRAINT unique_luid UNIQUE (luid);
    
    DROP TABLE record_status; -- earlier attempt
    
    CREATE TABLE record_status
    (
      luid integer NOT NULL REFERENCES jgi_metadata(luid),
      status_code char(1) NOT NULL DEFAULT 'Q',
      status text,
      PRIMARY KEY (luid)
    );
    ALTER TABLE record_status ADD CONSTRAINT status_ck CHECK (status_code = ANY (ARRAY['Q'::char(1), 'P'::char(1), 'E'::char(1)]));
    
    INSERT INTO record_status (luid,status) SELECT luid,status FROM jgi_metadata;
    
    UPDATE record_status SET status_code='Q' WHERE status='QUEUED';
    UPDATE record_status SET status_code='P' WHERE status='PROCESSED';
    UPDATE record_status SET status_code='E' WHERE status='ERROR';
    
    ALTER TABLE record_status DROP COLUMN status;
    
    CREATE INDEX ON record_status(luid);
    CREATE INDEX ON record_status(status_code);
    
    ALTER TABLE jgi_metadata DROP COLUMN status;
    

    Tabela "public.record_status"

       Column    |     Type     | Collation | Nullable |   Default   | Storage  | Stats target | Description
    -------------+--------------+-----------+----------+-------------+----------+--------------+-------------
     luid        | integer      |           | not null |             | plain    |              |
     status_code | character(1) |           | not null | 'Q'::bpchar | extended |              |
    Indexes:
        "record_status_pkey" PRIMARY KEY, btree (luid)
        "record_status_luid_idx" btree (luid)
        "record_status_status_code_idx" btree (status_code)
    Check constraints:
        "status_ck" CHECK (status_code = ANY (ARRAY['Q'::character(1), 'P'::character(1), 'E'::character(1)]))
    Foreign-key constraints:
        "record_status_luid_fkey" FOREIGN KEY (luid) REFERENCES metadata(luid)
    

    Também simplifiquei a visualização de estatísticas removendo a fast_count_rows()função (que usava bigint), o que cortou outros 500 ms da consulta:

    Exibir "public.metadata_queue_statistics"

    CREATE OR REPLACE VIEW metadata_queue_statistics AS
     SELECT count(*) FILTER (WHERE record_status.status_code = 'Q'::char(1)) AS queued,
            count(*) FILTER (WHERE record_status.status_code = 'P'::char(1)) AS completed,
            count(*) FILTER (WHERE record_status.status_code = 'E'::char(1)) AS failed,
            count(*) AS total
      FROM record_status
    ;
    

    Desempenho antes de remover fast_count_rowsa função da exibição:

    corpus=# EXPLAIN ANALYZE SELECT * FROM metadata_queue_statistics;
                                                              QUERY PLAN
    
    -------------------------------------------------------------------------------------------------------------------------------
     Aggregate  (cost=170413.05..170413.56 rows=1 width=32) (actual time=828.100..828.101 rows=1 loops=1)
       ->  Seq Scan on record_status  (cost=0.00..98880.42 rows=4768842 width=2) (actual time=0.009..337.786 rows=4736786 loops=1)
     Planning Time: 0.227 ms
     Execution Time: 828.123 ms
    (4 rows)
    

    Desempenho após remover fast_count_rowsa função da visualização:

    corpus=# EXPLAIN ANALYZE SELECT * FROM metadata_queue_statistics;
                                                              QUERY PLAN
    
    ----------------------------------------------------------------------------------------------------------------------------------------------------
     Finalize Aggregate  (cost=106835.22..106835.23 rows=1 width=32) (actual time=360.787..379.916 rows=1 loops=1)
       ->  Gather  (cost=106834.99..106835.20 rows=2 width=32) (actual time=360.502..379.910 rows=3 loops=1)
             Workers Planned: 2
             Workers Launched: 2
             ->  Partial Aggregate  (cost=105834.99..105835.00 rows=1 width=32) (actual time=349.462..349.462 rows=1 loops=3)
                   ->  Parallel Seq Scan on record_status  (cost=0.00..71062.18 rows=1987018 width=2) (actual time=0.181..164.633 rows=1578929 loops=3)
     Planning Time: 0.083 ms
     Execution Time: 379.953 ms
    (8 rows)
    

    Então, agora tenho essa visualização concluída em menos de meio segundo.

    Para comparação com os tamanhos de buffer das minhas tentativas anteriores de resolver este problema, aqui está o plano de execução mais detalhado:

    corpus=# EXPLAIN (ANALYZE, BUFFERS, VERBOSE) SELECT * FROM metadata_queue_statistics;
    
                     QUERY PLAN
    
    ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
     Finalize Aggregate  (cost=106835.22..106835.23 rows=1 width=32) (actual time=402.090..406.696 rows=1 loops=1)
       Output: count(*) FILTER (WHERE (record_status.status_code = 'Q'::character(1))), count(*) FILTER (WHERE (record_status.status_code = 'P'::character(1))), count(*) FILTER (WHERE (record_status.status_code = 'E'::character(1))), count(*)
       Buffers: shared hit=51192
       ->  Gather  (cost=106834.99..106835.20 rows=2 width=32) (actual time=382.500..406.686 rows=3 loops=1)
             Output: (PARTIAL count(*) FILTER (WHERE (record_status.status_code = 'Q'::character(1)))), (PARTIAL count(*) FILTER (WHERE (record_status.status_code = 'P'::character(1)))), (PARTIAL count(*) FILTER (WHERE (record_status.status_code = 'E'::character(1)))), (PARTIAL count(*))
             Workers Planned: 2
             Workers Launched: 2
             Buffers: shared hit=51192
             ->  Partial Aggregate  (cost=105834.99..105835.00 rows=1 width=32) (actual time=378.003..378.003 rows=1 loops=3)
                   Output: PARTIAL count(*) FILTER (WHERE (record_status.status_code = 'Q'::character(1))), PARTIAL count(*) FILTER (WHERE (record_status.status_code = 'P'::character(1))), PARTIAL count(*) FILTER (WHERE (record_status.status_code = 'E'::character(1))), PARTIAL count(*)
                   Buffers: shared hit=51192
                   Worker 0: actual time=366.127..366.128 rows=1 loops=1
                     Buffers: shared hit=6728
                   Worker 1: actual time=385.566..385.566 rows=1 loops=1
                     Buffers: shared hit=7750
                   ->  Parallel Seq Scan on public.record_status  (cost=0.00..71062.18 rows=1987018 width=2) (actual time=1.255..188.238 rows=1578929 loops=3)
                         Output: record_status.status_code
                         Buffers: shared hit=51192
                         Worker 0: actual time=3.727..181.228 rows=1111377 loops=1
                           Buffers: shared hit=6728
                         Worker 1: actual time=0.030..211.375 rows=1073500 loops=1
                           Buffers: shared hit=7750
     Planning Time: 0.079 ms
     Execution Time: 406.726 ms
    (24 rows)
    
    • 0
  4. Take Ichiru
    2025-02-27T01:22:51+08:002025-02-27T01:22:51+08:00

    Não sei como foi, mas preciso saber o seguinte:

    • Qual é a sua versão do PostgreSQL e o arquivo de configuração? Seu hardware, o tipo de disco do seu volume de dados/índice ou partição

    Existem tantas variáveis ​​que eu resumiria em duas questões:

    (1) Como @Stanislav disse acima, a cardinalidade do índice de status é muito baixa para se beneficiar do índice baseado em heap.

    (2) A tabela tem muitos campos TEXT e pode aumentar a linha em cada página apenas para fornecer um número exato de linhas e, por causa da coluna TEXT no meio da linha inteira, seu desempenho seria ruim, explicando que seu ANALYZE o torna bom no início e, em seguida, despeja tudo dos shared_buffers.

    --> For the second issue, here is what I did, create two tables, one to store its critical result such as media_type, status, thing .. that technically don't have TEXT field in it. And the other has UNIQUE FOREIGN KEY with PRIMARY KEY from the first table.

    --> For the INSERT and UPDATE and DELETE, in your app, making an explicit transaction include two INSERT (or UPDATE/DELETE) in it. If both are OK, apply COMMIT and/or ROLLBACK if you don't like it. Then if you want to extract it content, use LEFT JOIN (not INNER JOIN). In here, the main (first) table is much smaller, allowing you to load and get the COUNT on the same amount of rows in less number of pages required to scan.

    • 0

relate perguntas

  • SQL obtendo IDs de uma tabela com várias entradas em outra tabela

  • Tabela chave/valor com limitadores

  • selecionando linhas que têm n condições de chave estrangeira satisfeitas

  • Qual é o nome desse tipo de consulta e qual é um exemplo eficiente?

  • Como (e por que) o TOP impacta um plano de execução?

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