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 / 254268
Accepted
TmTron
TmTron
Asked: 2019-11-28 09:01:38 +0800 CST2019-11-28 09:01:38 +0800 CST 2019-11-28 09:01:38 +0800 CST

Como usar o índice para junção de intervalo de tempo simples?

  • 772

Eu tenho uma tabela de série temporal grande (~ 100 milhões de linhas) t_16no Postgres 11.5 , onde a chave primária é um campo abs_date_timedo tipo timestamp.

Esta é uma continuação desta pergunta:

  • Por que um CTE causa uma varredura completa da tabela?

Inicialmente pensei que estava relacionado a um CTE. Mas esta consulta é lenta, mesmo sem CTE.

Como posso fazer a consulta a seguir usar a chave primária index , para evitar uma verificação completa da tabela?

tsrange

Esta consulta leva ~ 20 segundos no meu PC de desenvolvimento:

SELECT t_16_gen.*
FROM t_16_gen,
     (VALUES (tsrange('["2019-11-26 12:00:00","2019-11-26 12:00:15")'))
           , (tsrange('["2019-11-26 13:00:00","2019-11-26 13:00:15")'))) as ranges (time_range)
WHERE (abs_date_time >= LOWER(ranges.time_range)
    AND abs_date_time <  UPPER(ranges.time_range));

Explique o plano:

Gather  (cost=1000.00..6185287.15 rows=20571433 width=80)
  Workers Planned: 2
  ->  Nested Loop  (cost=0.00..4127143.85 rows=8571430 width=80)
        Join Filter: ((t_16_gen.abs_date_time >= lower("*VALUES*".column1)) AND (t_16_gen.abs_date_time < upper("*VALUES*".column1)))
        ->  Parallel Seq Scan on t_16_gen  (cost=0.00..1620000.38 rows=38571438 width=80)
        ->  Values Scan on "*VALUES*"  (cost=0.00..0.03 rows=2 width=32)

Na produção, o conjunto de tsranges vem de uma UDF - mas sempre haverá apenas alguns intervalos (<200) e cada intervalo terá menos de 1500 linhas e os intervalos não se sobreporão.

Timestamps simples em vez detsrange

Quando usamos timestamps diretamente (ou seja, não usando tsrange LOWER()e UPPER()), a consulta já é mais rápida. Esta consulta leva ~ 7 segundos no meu PC de desenvolvimento:

SELECT t_16_gen.*
FROM t_16_gen,
     (VALUES ('2019-11-26 12:00:00'::timestamp,'2019-11-26 12:00:15'::timestamp)
           , ('2019-11-26 13:00:00','2019-11-26 13:00:15')) as ranges (start_incl, end_excl)
WHERE (abs_date_time >= ranges.start_incl
    AND abs_date_time <  ranges.end_excl);

Explique o plano:

Nested Loop  (cost=0.00..5400001.28 rows=20571433 width=80)
  Join Filter: ((t_16_gen.abs_date_time >= "*VALUES*".column1) AND (t_16_gen.abs_date_time < "*VALUES*".column2))
  ->  Seq Scan on t_16_gen  (cost=0.00..2160000.50 rows=92571450 width=80)
  ->  Materialize  (cost=0.00..0.04 rows=2 width=16)
        ->  Values Scan on "*VALUES*"  (cost=0.00..0.03 rows=2 width=16)

ORcondições = RÁPIDO

Quando reescrevo a consulta para usar condições OR , é rápido. Esta consulta leva ~ 200ms no meu PC de desenvolvimento:

SELECT t_16_gen.*
FROM t_16_gen
WHERE (abs_date_time >= '2019-11-26 12:00:00' AND abs_date_time < '2019-11-26 12:00:15')
   OR (abs_date_time >= '2019-11-26 13:00:00' AND abs_date_time < '2019-11-26 13:00:15');

Explique o plano:

Gather  (cost=13326.98..1533350.92 rows=923400 width=80)
  Workers Planned: 2
  ->  Parallel Bitmap Heap Scan on t_16_gen  (cost=12326.98..1440010.92 rows=384750 width=80)
        Recheck Cond: (((abs_date_time >= '2019-11-26 12:00:00'::timestamp without time zone) AND (abs_date_time < '2019-11-26 12:00:15'::timestamp without time zone)) OR ((abs_date_time >= '2019-11-26 13:00:00'::timestamp without time zone) AND (abs_date_time < '2019-11-26 13:00:15'::timestamp without time zone)))
        ->  BitmapOr  (cost=12326.98..12326.98 rows=925714 width=0)
              ->  Bitmap Index Scan on t_16_pkey  (cost=0.00..5932.64 rows=462857 width=0)
                    Index Cond: ((abs_date_time >= '2019-11-26 12:00:00'::timestamp without time zone) AND (abs_date_time < '2019-11-26 12:00:15'::timestamp without time zone))
              ->  Bitmap Index Scan on t_16_pkey  (cost=0.00..5932.64 rows=462857 width=0)
                    Index Cond: ((abs_date_time >= '2019-11-26 13:00:00'::timestamp without time zone) AND (abs_date_time < '2019-11-26 13:00:15'::timestamp without time zone))

UNION= RÁPIDO

Quando reescrevo a consulta para usar condições UNION , também é rápido. Esta consulta leva ~ 220ms no meu PC de desenvolvimento:

SELECT t_16_gen.*
FROM t_16_gen
WHERE (abs_date_time >= '2019-11-26 12:00:00' AND abs_date_time < '2019-11-26 12:00:15')
UNION
SELECT t_16_gen.*
FROM t_16_gen
WHERE (abs_date_time >= '2019-11-26 13:00:00' AND abs_date_time < '2019-11-26 13:00:15');

Explique o plano:

Unique  (cost=1032439.64..1069468.20 rows=925714 width=80)
  ->  Sort  (cost=1032439.64..1034753.93 rows=925714 width=80)
"        Sort Key: t_16_gen.abs_date_time, t_16_gen.c_422, t_16_gen.c_423, t_16_gen.c_424, t_16_gen.c_425, t_16_gen.c_426, t_16_gen.c_427, t_16_gen.c_428, t_16_gen.c_429, t_16_gen.c_430, t_16_gen.c_431, t_16_gen.c_432, t_16_gen.c_433, t_16_gen.c_434, t_16_gen.c_435"
        ->  Append  (cost=0.57..892513.13 rows=925714 width=80)
              ->  Index Scan using t_16_pkey on t_16_gen  (cost=0.57..439313.71 rows=462857 width=80)
                    Index Cond: ((abs_date_time >= '2019-11-26 12:00:00'::timestamp without time zone) AND (abs_date_time < '2019-11-26 12:00:15'::timestamp without time zone))
              ->  Index Scan using t_16_pkey on t_16_gen t_16_gen_1  (cost=0.57..439313.71 rows=462857 width=80)
                    Index Cond: ((abs_date_time >= '2019-11-26 13:00:00'::timestamp without time zone) AND (abs_date_time < '2019-11-26 13:00:15'::timestamp without time zone))

Reproduzindo o problema

Para reproduzir o problema, posso criar uma nova tabela e preenchê-la com dados fictícios. Em seguida, reinicie o banco de dados antes de cada teste, para que os dados não sejam armazenados em cache.
Nota: a consulta de inserção pode ser executada por vários minutos!

create table if not exists t_16_gen (
    abs_date_time timestamp constraint t_16_pkey primary key,
    c_422 bigint,
    c_423 bigint,
    c_424 real,
    c_425 real,
    c_426 real,
    c_427 real,
    c_428 real,
    c_429 real,
    c_430 bigint,
    c_431 real,
    c_432 real,
    c_433 real,
    c_434 bigint,
    c_435 real
);

INSERT INTO t_16_gen
SELECT ts, 1,2,3,4,5,6,7,8,9,10,11,12,13,14
FROM (SELECT generate_series('2019-11-26'::timestamp, '2019-11-27', '1 millisecond') as ts) as gs;
postgresql index
  • 1 1 respostas
  • 789 Views

1 respostas

  • Voted
  1. Best Answer
    Erwin Brandstetter
    2019-11-28T13:16:18+08:002019-11-28T13:16:18+08:00

    Sua última consulta (rápida) tem duas condições idênticas WHERE, que o Postgres é capaz de identificar e dobrar para um . Daí o plano mais simples com apenas uma única condição de índice.

    Fica mais caro com várias condições diferentes . Mas o Postgres ainda continua operando com base em estimativas para valores reais de entrada. Tente com um ou mais intervalos grandes na WHEREcláusula incluindo a maior parte ou toda a tabela e você verá uma varredura sequencial.

    Isso é diferente em princípio para suas duas primeiras consultas com base em uma VALUESexpressão. Lá, o Postgres bifurca dois casos:

    • Para uma linha de entrada, o Postgres analisa os valores reais e produz o mesmo plano da terceira consulta com uma única WHEREcondição, com estimativas baseadas nos valores reais de entrada. Você obtém índice / índice de bitmap / varredura sequencial de acordo.
    • Para mais de uma linha de entrada, o Postgres para de olhar para valores individuais e prepara um plano de consulta com base em estimativas genéricas e no número real de linhas de entrada. Você pode fornecer uma VALUESexpressão com 5 linhas resultando em nenhum resultado ou 5 linhas retornando a tabela inteira, será o mesmo plano de consulta.

    Testado no Postgres 11.

    Também esteja ciente de que unir a um conjunto (a VALUESexpressão) é logicamente diferente de adicionar vários ORpredicados de intervalo 'ed. As linhas que correspondem a vários intervalos de tempo no conjunto são retornadas várias vezes, enquanto o segundo formulário retorna apenas uma única instância, mesmo que corresponda a vários predicados.

    Assim, a segunda forma com muitos OR's naturalmente favorece as varreduras de índice de bitmap, que dobram vários hits em um automaticamente. O Postgres não tem ideia de que suas condições nunca se sobrepõem. (Ou eles vão? Então você tem um problema maior.) Se os dados em sua tabela são classificados fisicamente por tempo (correspondendo à sua coluna PK abs_date_time), então isso ainda deve funcionar a seu favor por acidente .

    Mas como suas linhas são bastante largas (menos tuplas por página) e se você tiver muitos intervalos de tempo (até 200?), favorecer indevidamente as varreduras de índice de bitmap pode ser uma desvantagem afinal e as varreduras de índice simples podem ser mais rápidas.

    Solução

    UNION ALLdeve ser superior para você!

    SELECT * FROM s_28.t_16 WHERE abs_date_time >= '2019-11-26 12:00:00' AND abs_date_time < '2019-11-26 12:10:00'
    UNION ALL
    SELECT * FROM s_28.t_16 WHERE abs_date_time >= '2019-11-26 13:00:00' AND abs_date_time < '2019-11-26 13:10:00'
    -- add (many) more
    ;
    

    Em primeiro lugar, é a melhor combinação para a lógica em funcionamento aqui. As chances são muito maiores de que versões futuras do Postgres continuem usando bons planos de consulta.

    Dessa forma, o Postgres usa estimativas baseadas na entrada real para cada um SELECT- e dadas suas especificações (todos os intervalos são pequenos), a consulta nunca deve degradar para varreduras sequenciais, desde que as estatísticas da sua tabela não sejam completamente enganosas.

    E as varreduras de índice não estão mais em desvantagem (injusta) em relação às varreduras de índice de bitmap.

    • 2

relate perguntas

  • Quanto "Padding" coloco em meus índices?

  • Sequências Biológicas do UniProt no PostgreSQL

  • O que significa "índice" em RDBMSs? [fechado]

  • Como criar um índice condicional no MySQL?

  • 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