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 / 334684
Accepted
derteufelqwe
derteufelqwe
Asked: 2024-01-09 18:07:53 +0800 CST2024-01-09 18:07:53 +0800 CST 2024-01-09 18:07:53 +0800 CST

Postgres filtra coluna timestamptz por data muito lenta (possivelmente índice não usado?)

  • 772

Eu tenho a seguinte tabela com 462541359 linhas.

create table "Prices"
(
    "Id"            bigint generated by default as identity
        constraint "PK_Prices"
            primary key,
    "Timestamp"     timestamp with time zone not null,
    "DieselPrice"   real                     not null,
    "E5Price"       real                     not null,
    "E10Price"      real                     not null,
    "DieselChanged" boolean                  not null,
    "E5Changed"     boolean                  not null,
    "E10Changed"    boolean                  not null,
    "StationId"     uuid                     not null
        constraint "FK_Prices_Stations_StationId"
            references "Stations"
            on delete cascade
);

alter table "Prices"
    owner to postgres;

create index "IX_Prices_DieselChanged"
    on "Prices" ("DieselChanged");

create index "IX_Prices_E10Changed"
    on "Prices" ("E10Changed");

create index "IX_Prices_E5Changed"
    on "Prices" ("E5Changed");

create index "IX_Prices_StationId"
    on "Prices" ("StationId");

create index "IX_Prices_Timestamp"
    on "Prices" ("Timestamp");

Reduzi minha consulta a isso (como um exemplo mínimo)

select
    count(*)
FROM "Prices"
where "StationId" = 'f38e56c1-e9ba-428f-adb0-bdefa428559b'
  and "Timestamp" >= '2023-01-07'

onde StationIdestá apenas um dos 17.000 IDs de estação.

Quando filtro esta tabela, obtenho um desempenho ruim (aproximadamente 8s) na execução inicial. Quando executo a consulta novamente, ela fica muito mais rápida (aproximadamente 300 ms). Quando altero a StationIdprimeira consulta fica lenta novamente.

Tentei analisar o desempenho usando EXPLAIN (ANALYZE, BUFFERS)e obtive o seguinte resultado

Aggregate  (cost=124159.66..124159.67 rows=1 width=8) (actual time=7734.619..7734.620 rows=1 loops=1)
  Buffers: shared read=38398
  ->  Bitmap Heap Scan on ""Prices""  (cost=358.69..124141.54 rows=7246 width=0) (actual time=6668.499..7732.704 rows=9678 loops=1)
        Recheck Cond: (""StationId"" = 'b07d169a-2856-4903-baee-d17e496ebfd0'::uuid)
        Filter: (""Timestamp"" >= '2023-01-07 00:00:00+00'::timestamp with time zone)
        Rows Removed by Filter: 28645
        Heap Blocks: exact=38323
        Buffers: shared read=38398
        ->  Bitmap Index Scan on ""IX_Prices_StationId""  (cost=0.00..356.88 rows=33107 width=0) (actual time=21.983..21.983 rows=38364 loops=1)"
              Index Cond: (""StationId"" = 'b07d169a-2856-4903-baee-d17e496ebfd0'::uuid)
              Buffers: shared read=34
Planning Time: 0.082 ms
JIT:
  Functions: 7
  Options: Inlining false, Optimization false, Expressions true, Deforming true
  Timing: Generation 0.296 ms, Inlining 0.000 ms, Optimization 0.196 ms, Emission 2.469 ms, Total 2.961 ms
Execution Time: 7734.984 ms

Pelo que li, o Bitmap Heap Scaníndice da Timestampcoluna está sendo usado apenas parcialmente e suspeito que essa seja a causa do baixo desempenho.

Qual pode ser a causa da lentidão inicial da consulta e como posso utilizar melhor os índices para acelerar a filtragem por data? E como posso garantir que manterei o desempenho do índice ao combinar meu filtro com mais filtros como E5Changed?

postgresql
  • 1 1 respostas
  • 43 Views

1 respostas

  • Voted
  1. Best Answer
    bobflux
    2024-01-09T19:03:24+08:002024-01-09T19:03:24+08:00

    Aqui está a explicação com uma boa formatação em depesz .

    Portanto, a varredura de bitmap usa o índice em StationId para sinalizar 38.364 linhas. Isso lê quase o mesmo número de buffers, o que significa que os dados provavelmente foram inseridos na ordem do carimbo de data/hora, espalhando linhas com qualquer StationId individual por toda a tabela, o que geralmente é o caso com dados de séries temporais.

    Esse grande número de leituras aleatórias explica por que a consulta é lenta, principalmente se você não estiver usando SSDs.

    Então, 75% dessas linhas não satisfazem a condição de carimbo de data/hora, portanto, apenas 25% das linhas são mantidas.

    Agora, por que não usa o índice no carimbo de data/hora? Supondo que os carimbos de data/hora sejam distribuídos da mesma maneira para todos os StationIds, isso significa que sua condição de carimbo de data/hora atingiria 25% das linhas. Se fizesse um BitmapAnd para combinar índices em Timestamp e StationId, então teria que varrer todas as linhas do índice que satisfazem a condição no índice StationId e no índice Timestamp e combinar os dois em um bitmap. No índice Timestamp, isso seria 25% de 462541359, ou cerca de 115 milhões de linhas do índice. O Postgres faz uma suposição razoável de que essa não será a opção mais rápida, por isso escolhe outro plano, que é o que você obteve.

    Uma opção muito melhor seria um índice de múltiplas colunas em (StationId,Timestamp) ou (Timestamp,StationId), que satisfaria as condições diretamente com uma varredura de índice.

    Mas... qual você deve escolher? Um índice em (a,b) também é um índice em (a), mas não um índice em (b). Assim, o índice multicoluna escolhido substituirá um dos índices existentes, seja em StationId ou Timestamp.

    Um índice em (a,b) permite pesquisas de intervalo em (a) e (a,b), mas não apenas em (b). Assim como uma lista telefônica classificada por (sobrenome, nome) otimiza pesquisas como "o sobrenome é 'Smith' e o primeiro_nome começa com 'A' ou 'B'" porque todas as linhas que satisfazem são agrupadas como um intervalo dentro do índice .

    Como StationId é um uuid, você nunca fará consultas de intervalo nele. Mas provavelmente você fará consultas combinando "SiationId=constant" e "Timestamp in a range", usando "<" ou BETWEEN. Portanto, faz muito mais sentido criar um índice em (StationId,Timestamp) que irá otimizá-los.

    alter table "Prices" owner to postgres;
    

    É melhor usar um usuário normal em vez do postgres, por motivos de segurança.

    create index "IX_Prices_DieselChanged" on "Prices" ("DieselChanged");
    create index "IX_Prices_E10Changed" on "Prices" ("E10Changed");
    create index "IX_Prices_E5Changed" on "Prices" ("E5Changed");
    

    Índices não seletivos nunca serão utilizados, portanto são um desperdício de recursos.

    Se a distribuição estatística da coluna bool não for algo como 99% para um valor e 1% para o outro, o índice é inútil. Ao contrário de um índice no carimbo de data/hora que pode ser usado para escolher um carimbo de data/hora entre milhões na tabela, bool tem apenas dois valores possíveis...

    Se sua tabela tiver apenas uma pequena porcentagem de linhas com a coluna bool definida como verdadeira, isso poderá ser seletivo o suficiente. Mas, neste caso, a grande maioria das linhas do índice tem o bool definido como falso e está desperdiçando espaço. Neste caso é melhor criar um índice condicional em (bool_column) WHERE bool_column. Portanto, o índice armazenará apenas as linhas com o valor “true”. (Ou WHERE não bool_column, se for mais seletivo). Mas geralmente índices de coluna única em bool não são úteis.

    Mesmo que a coluna bool seja muito seletiva, provavelmente seria mais eficiente criar um índice condicional em (StationId,Timestamp) WHERE bool_column se você os pesquisar com frequência em suas consultas. Isso também funcionará como um índice simples como (bool_column) WHERE bool_column.

    Se sua tabela ficar enorme, você também poderá particioná-la por carimbo de data/hora (digamos, por mês). Então, você pode usar CLUSTER em partições antigas para reordenar as linhas no disco na ordem (StationId, Timestamp), o que tornará a distribuição das linhas na tabela muito mais amigável para a consulta na questão. No entanto, como as linhas não são mais classificadas por carimbo de data/hora, isso terá o efeito oposto em consultas usando apenas condições de carimbo de data/hora e não de StationId.

    Ou você pode usar um banco de dados especializado em séries temporais, como o clickhouse:

    select topic, count(*) from mqtt_float group by topic order by 2 desc limit 3;
    
    ┌─topic──────────────────────────────────────┬───count()─┐
    │ pv/meter/total_power                       │ 232506715 │
    │ pv/meter/phase_1_current                   │  50604345 │
    │ pv/meter/phase_2_current                   │  49684640 │
    └────────────────────────────────────────────┴───────────┘
    
    5 rows in set. Elapsed: 0.979 sec. Processed 1.58 billion rows, 1.58 GB (1.61 billion rows/s., 1.61 GB/s.)
    
    select count(*) from mqtt_float where topic='pv/meter/phase_1_current' and ts > '2023-10-01';
    
    ┌──count()─┐
    │ 13054580 │
    └──────────┘
    
    1 row in set. Elapsed: 0.019 sec. Processed 13.12 million rows, 118.07 MB (685.46 million rows/s., 6.17 GB/s.)
    

    Isso tem outro conjunto completamente diferente de compromissos e casos de uso, é claro. Por exemplo, não faz atualizações, apenas inserções. Existem vários bancos de dados especializados em séries temporais, todos com suas peculiaridades e conjuntos de compromissos.

    • 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