Eu tenho algumas perguntas sobre o trabalho de índices no PostgreSQL. Tenho uma Friends
tabela com o seguinte índice:
Friends ( user_id1 ,user_id2)
user_id1
e user_id2
são chaves estrangeiras para a user
tabela
Estes são equivalentes? Se não, então por quê?
Index(user_id1,user_id2) and Index(user_id2,user_id1)
Se eu criar a chave primária (user_id1,user_id2), ela cria automaticamente índices para ela e
Se os índices na primeira pergunta não forem equivalentes, qual índice é criado no comando de chave primária acima?
Esta resposta é sobre índices de árvore B (padrão) . Veja isso mais tarde, resposta relacionada sobre GiST, GIN etc.:
Aqui estão os resultados da consulta de uma tabela na segunda coluna de um índice de várias colunas .
Os efeitos são fáceis de reproduzir para qualquer um. Experimente em casa.
Testei com o PostgreSQL 9.0.5 no Debian usando uma tabela de tamanho médio de um banco de dados real com 23322 linhas. Ele implementa o relacionamento n:m entre as tabelas
adr
(endereço) eatt
(atributo), mas isso não é relevante aqui. Esquema simplificado:A
UNIQUE
restrição implementa efetivamente um índice exclusivo. Repeti o teste com um índice simples para ter certeza e obtive resultados idênticos como esperado.A tabela está agrupada no
adratt_uni
índice e antes do teste eu executei:As varreduras sequenciais para consultas
(adr_id, att_id)
são tão rápidas quanto possível. O índice de várias colunas ainda pode ser usado para uma condição de consulta apenas na segunda coluna de índice.Executei as consultas algumas vezes para preencher o cache e escolhi a melhor das dez execuções para obter resultados comparáveis.
1. Consulta usando as duas colunas
Saída de
EXPLAIN ANALYZE
:2. Consulta usando a primeira coluna
Saída de
EXPLAIN ANALYZE
:3. Consulta usando a segunda coluna
Saída de
EXPLAIN ANALYZE
:4. Desabilite indexscan e bitmapscan
Saída de EXPLICAR ANALISAR:
Saída de
EXPLAIN ANALYZE
:Conclusão
Conforme esperado, o índice de várias colunas é usado para uma consulta somente na segunda coluna.
Como esperado, é menos eficiente, mas a consulta ainda é 3x mais rápida do que sem o índice.
Após desabilitar as verificações de índice, o planejador de consulta escolhe uma verificação de heap de bitmap, que executa quase tão rápido. Somente depois de desabilitar isso também, ele volta para uma verificação sequencial.
Veja outra resposta para a citação original do manual.
Atualizações desde o Postgres 9.0
Tudo ainda é basicamente verdade no Postgres 13. Mudanças mais notáveis:
INCLUDE
chave no Postgres 11Todos a favor do desempenho do índice. (As varreduras sequenciais também ficaram mais rápidas.)
1) Sim e não.
Para uma consulta que usa ambas as colunas, por exemplo
where (user_id1, user_id2) = (1,2)
, não importa qual índice é criado.Para uma consulta que tem uma condição em apenas uma das colunas, por exemplo
where user_id1 = 1
, isso importa porque geralmente apenas as colunas "principais" podem ser usadas para uma comparação pelo otimizador. Entãowhere user_id1 = 1
seria capaz de usar o índice (user_id1, user_id2), mas não seria capaz de um índice (user_id2, user_id1) para todos os casos.Depois de brincar com isso (depois que Erwin tão gentilmente nos mostrou uma configuração onde funciona), parece que isso depende muito da distribuição de dados da segunda coluna, embora eu ainda não tenha descoberto qual situação permite que o otimizador use colunas à direita para uma condição WHERE.
Oracle 11 que também pode (às vezes) usar colunas que não estão no início da definição do índice.
re 2) Sim, ele criará um índice
Citação do manual
re 2a)
Primary Key (user_id1,user_id2)
criará um índice em (user_id1,user_id2) (que você pode descobrir sozinho com muita facilidade simplesmente criando uma chave primária)Eu recomendo que você leia o capítulo sobre índices no manual , basicamente responde a todas as perguntas acima.
Além disso, qual índice criar? by depesz faz um bom trabalho explicando a ordem nas colunas de índice e outros tópicos relacionados ao índice.
Anúncio 1)
Existem limitações no PostgreSQL como @a_horse_with_no_name descreve . Até a versão 8.0, os índices de várias colunas só podiam ser usados para consultas na(s) coluna(s) inicial(is). Isso foi melhorado na versão 8.1. O manual atual do Postgres 14 (atualizado) explica:
Destaque meu. Posso confirmar isso por experiência.
Veja também o caso de teste adicionado minha resposta posterior aqui .
Isso é uma resposta à resposta de Jack , um comentário não faria.
Não havia índices de cobertura no PostgreSQL antes da versão 9.2. Devido ao modelo MVCC, cada tupla no conjunto de resultados deve ser visitada para verificar a visibilidade. Você pode estar pensando na Oracle.
Os desenvolvedores do PostgreSQL falam sobre "varreduras somente de índice" . Na verdade, o recurso foi lançado com o Postgres 9.2. Leia a mensagem de confirmação .
Depesz escreveu um post muito informativo no blog .
Índices de cobertura verdadeiros (atualização) são introduzidos com a
INCLUDE
cláusula com o Postgres 11. Relacionado:Isso também está um pouco fora:
Conforme relatado nos comentários da minha outra resposta, também executei testes com uma tabela de dois inteiros e nada mais. O índice contém as mesmas colunas que a tabela. O tamanho de um índice btree é cerca de 2/3 do tamanho da tabela. Não o suficiente para explicar um speedup de fator 3. Fiz mais testes, baseado em sua configuração, simplificado para duas colunas e com 100.000 linhas. Na minha instalação do PostgreSQL 9.0 os resultados foram consistentes.
Se a tabela tiver colunas adicionais, o speedup com índice se torna mais substancial, mas certamente não é o único fator aqui .
Resumo
Multi-column indexes can be used for selective criteria with queries on non-leading columns, but the speedup is only a low factor depending on table and index tuple size and visibility. Higher for wider rows, lower for larger portions of the table in the result set.
Create an additional index with those columns first if performance is important.
If all involved columns are included in an index (covering index) and all involved rows (per block) are visible to all transactions, you can get an "index-only scan" in Postgres 9.2 or later.
Estes não são equivalentes e, de um modo geral, index(bar,baz) não será eficiente para consultas do formulário
select * from foo where baz=?
Erwin demonstrou que esses índices podem realmente acelerar uma consulta, mas esse efeito é limitado e não é da mesma ordem que você geralmente espera que um índice melhore uma pesquisa - ele se baseia no fato de que uma 'varredura completa' de um índice geralmente é mais rápido do que uma 'varredura completa' da tabela indexada devido às colunas extras na tabela que não aparecem no índice.
Resumo: os índices podem ajudar as consultas mesmo em colunas não iniciais, mas de uma das duas maneiras secundárias e relativamente menores e não da maneira dramática que você normalmente espera que um índice ajude devido à sua estrutura btree
nb as duas maneiras pelas quais o índice pode ajudar são se uma varredura completa do índice for significativamente mais barata do que uma varredura completa da tabela e: 1. as pesquisas de tabela são baratas (porque são poucas ou estão agrupadas)
ou 2. o índice está cobrindo , então não há pesquisas de tabela em todos osoops, veja os comentários de Erwins aquibanco de teste:
query 1 (sem índice, atingindo 74 buffers ):
consulta 2 (com índice - o otimizador ignora o índice - atingindo 74 buffers novamente):
consulta 2 (com índice - e enganamos o otimizador para usá-lo):
Portanto, o acesso através do índice é duas vezes mais rápido, neste caso, atingindo 30 buffers - o que em termos de indexação é 'um pouco mais rápido'!, e YMMV dependendo do tamanho relativo da tabela e do índice, juntamente com o número de linhas filtradas e características de cluster dos dados da tabela
Por outro lado, as consultas na coluna inicial usam a estrutura btree do índice - neste caso, atingindo 2 buffers :