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 / 124982
Accepted
James Hay
James Hay
Asked: 2016-01-02 00:48:17 +0800 CST2016-01-02 00:48:17 +0800 CST 2016-01-02 00:48:17 +0800 CST

Índice não sendo usado na consulta SELECT

  • 772

Eu tenho uma tabela de cerca de 3,25 milhões de linhas com o seguinte formato no Postgres 9.4.1

CREATE TABLE stats
(
    id serial NOT NULL,
    type character varying(255) NOT NULL,
    "references" jsonb NOT NULL,
    path jsonb,
    data jsonb,
    "createdAt" timestamp with time zone NOT NULL,
    CONSTRAINT stats_pkey PRIMARY KEY (id)
)
WITH (
    OIDS=FALSE
);

O typeé uma string simples com no máximo 50 caracteres.

A referencescoluna é um objeto com uma lista de valores-chave. Basicamente, qualquer lista de valores de chave simples e apenas 1 nível de profundidade, os valores são sempre strings. Poderia ser

{
    "fruit": "plum"
    "car": "toyota"
}

ou pode ser

{
    "project": "2532"
}

O createdAttimestamp nem sempre é gerado a partir do banco de dados (mas será por padrão se um valor não for fornecido)

No momento, estou usando a tabela apenas com dados de teste. Nesses dados, cada linha tem uma projectchave como referência. Portanto, há 3,25 milhões de linhas com uma chave de projeto. Existem exatamente 400.000 valores distintos para a projectreferência. Existem apenas 5 valores distintos para o typecampo, isso provavelmente não passaria de algumas centenas em produção.

Então estou tentando indexar a tabela para realizar a seguinte query rapidamente:

SELECT
  EXTRACT(EPOCH FROM (MAX("createdAt") - MIN("createdAt"))) 
FROM
  stats
WHERE
  stats."references"::jsonb ? 'project' AND
  (
    stats."type" = 'event1' OR
    (
      stats."type" = 'event2' AND
      stats."createdAt" > '2015-11-02T00:00:00+08:00' AND
      stats."createdAt" < '2015-12-03T23:59:59+08:00'
    )
  )
GROUP BY stats."references"::jsonb->> 'project'

A consulta retorna a distância de tempo entre dois eventos com base em duas linhas de estatísticas que têm a mesma referência. Neste caso project. Sempre há apenas 1 linha para cada valor typeselecionado reference, mas também pode não haver linhas, caso em que o resultado retornado é 0 (isso é calculado posteriormente em uma parte diferente de uma consulta maior).

Eu criei um índice nas colunas createdAt typee references, mas o plano de execução da consulta parece estar fazendo uma verificação completa.

O índice

CREATE INDEX "stats_createdAt_references_type_idx"
    ON stats
    USING btree
    ("createdAt", "references", type COLLATE pg_catalog."default");

Plano de execução:

 HashAggregate  (cost=111188.31..111188.33 rows=1 width=38) 
                (actual time=714.499..714.499 rows=0 loops=1)
   Group Key: ("references" ->> 'project'::text)
      ->  Seq Scan on stats  (cost=0.00..111188.30 rows=1 width=38) 
                             (actual time=714.498..714.498 rows=0 loops=1)
          Filter: (
              (("references" ? 'project'::text) 
               AND ((type)::text = 'event1'::text)) OR 
              (((type)::text = 'event2'::text) 
               AND ("createdAt" > '2015-11-02 05:00:00+13'::timestamp with time zone) 
               AND ("createdAt" < '2015-12-04 04:59:59+13'::timestamp with time zone)))

Rows Removed by Filter: 3258680
Planning time: 0.163 ms
Execution time: 714.534 ms

Eu realmente não estou tão informado sobre planos de execução de indexação e consulta, então se alguém pudesse me indicar a direção certa, seria ótimo.

Editar

Conforme observado por Erwin, parece que mesmo se eu tivesse os índices corretos, uma verificação da tabela ainda ocorreria, pois a parte da tabela retornada da consulta é muito grande. Isso significa que, para esse conjunto de dados, esse é o tempo de consulta mais rápido que posso obter? Estou assumindo que se eu adicionasse mais 60 milhões de linhas não relacionadas sem uma referência de projeto, ele poderia usar um índice (se eu tivesse os índices corretos), mas não vejo como isso poderia acelerar a consulta adicionando mais dados. Talvez eu esteja perdendo alguma coisa.

database-design postgresql
  • 1 1 respostas
  • 674 Views

1 respostas

  • Voted
  1. Best Answer
    Erwin Brandstetter
    2016-01-02T20:20:07+08:002016-01-02T20:20:07+08:00

    De acordo com sua explicação atual, os índices não vão ajudar muito (se for o caso) com sua consulta atual.

    Portanto, há 3,25 milhões de linhas com uma chave de projeto.

    Esse também é o número total de linhas, portanto, esse predicado é truepara (quase) todas as linhas ... e não é nada seletivo. Mas não há nenhum índice útil para a jsonbcoluna "references". Incluí-lo no índice btree("createdAt", "references", type) é inútil.

    Mesmo se você tivesse um índice GIN geralmente mais útil "reference"como:

    CREATE INDEX stats_references_gix ON stats USING gin ("references");
    

    ... Postgres ainda não teria estatísticas úteis sobre chaves individuais dentro da jsonbcoluna.


    Existem apenas 5 valores distintos para otype

    Sua consulta seleciona tudo de um tipo e uma fração desconhecida de outro tipo. Isso é uma estimativa de 20 a 40% de todas as linhas . Uma varredura sequencial certamente será o plano mais rápido. Os índices começam a fazer sentido para cerca de 5% de todas as linhas ou menos .

    Para testar, você pode forçar um possível índice definindo para fins de depuração em sua sessão :

    SET enable_seqscan = off;
    

    Redefinir com:

    RESET enable_seqscan;
    

    Você verá consultas mais lentas...


    Você agrupa por valores de projeto:

    GROUP BY "references"->> 'project'

    E:

    Existem exatamente 400.000 valores distintos para a referência do projeto.

    São 8 linhas por projeto em média. Dependendo das frequências de valor, ainda temos que recuperar cerca de 3 a 20% de todas as linhas se apenas escolhermos mínimo e máximo por projeto em uma subconsulta LATERAL ...

    Experimente este índice, faz mais sentido do que o que você tem agora:

    CREATE INDEX stats_special_idx ON stats (type, ("references" ->> 'project'), "createdAt")
    WHERE "references" ? 'project';
    

    Postgres ainda pode cair para uma varredura sequencial ...

    Mais pode ser feito com um esquema normalizado / critérios mais seletivos / uma consulta mais inteligente que seleciona apenas o mínimo e o máximo "createdAt"...

    Consulta

    Eu escreveria sua consulta assim:

    SELECT EXTRACT(EPOCH FROM (MAX("createdAt") - MIN("createdAt"))) 
    FROM   stats
    WHERE  "references" ? 'project'
    AND   (type = 'event1' OR
           type = 'event2'
       AND "createdAt" >= '2015-11-02 00:00:00+08:00'  -- I guess you want this
       AND "createdAt" <  '2015-12-04 00:00:00+08:00'
          )
    GROUP  BY "references"->> 'project';  -- don't cast
    

    Notas

    • Não lance aqui:

      stats."references"::jsonb ? 'project'

      A coluna jsonbjá está, você não ganha nada. Se o predicado for seletivo, o uso do índice pode ser proibido pela conversão.

    • Seus predicados "createdAt"provavelmente estão incorretos nos limites inferior e superior. Para incluir dias inteiros, considere minha alternativa sugerida.

    • referencesé uma palavra reservada , então você sempre deve colocá-la entre aspas duplas. Não o use como identificador. Semelhante para nomes de casos CaMeL com aspas duplas como "createdAt"qualquer um. Permitido, mas propenso a erros, complicação desnecessária.

    • type

      type character varying(255) NOT NULL,

      O tipo é uma string simples com no máximo 50 caracteres.

      Existem apenas 5 valores distintos para o campo de tipo, isso provavelmente não seria mais do que algumas centenas em produção.

      Nada disso parece fazer sentido.

      • varchar(255)em si quase nunca faz sentido . 255 caracteres é um limite arbitrário sem significado no Postgres.
      • Se não tiver mais de 50 caracteres, o limite de 255 faz ainda menos sentido.
      • Em um projeto devidamente normalizado, você teria uma pequena integercoluna type_id(referindo-se a uma pequena typetabela) que ocupa apenas 4 bytes por linha e torna os índices menores e mais rápidos.
    • Idealmente, você teria uma projecttabela, listando todos os projetos e outra coluna FK inteira pequena project_idem formato stats. Faria qualquer consulta mais rápida. E para critérios seletivos, consultas muito mais rápidas seriam possíveis – mesmo sem a normalização sugerida. Ao longo destas linhas:

    • Otimize a consulta GROUP BY para recuperar o registro mais recente por usuário

    • 3

relate perguntas

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

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

  • Quais são algumas maneiras de implementar um relacionamento muitos-para-muitos em um data warehouse?

  • 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