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 pessoa. Apenas tente em casa.
Eu testei com o PostgreSQL 9.0.5 no Debian usando uma tabela de tamanho médio de um banco de dados da vida real com 23322 linhas. Ele implementa o relacionamento n: m entre as tabelas adr
(endereço) e att
(atributo), mas isso não é relevante aqui. Esquema simplificado:
CREATE TABLE adratt (
adratt_id serial PRIMARY KEY
, adr_id integer NOT NULL
, att_id integer NOT NULL
, log_up timestamp(0) NOT NULL DEFAULT (now())::timestamp(0)
, CONSTRAINT adratt_uni UNIQUE (adr_id, att_id)
);
A UNIQUE
restrição efetivamente implementa um índice exclusivo. Repeti o teste com um índice simples para ter certeza e obtive resultados idênticos aos esperados.
CREATE INDEX adratt_idx ON adratt(adr_id, att_id)
A tabela está agrupada no adratt_uni
índice e antes do teste eu executei:
CLUSTER adratt;
ANALYZE adratt;
As verificações sequenciais para consultas (adr_id, att_id)
são o mais rápido possível. O índice de várias colunas ainda será usado para uma condição de consulta apenas na segunda coluna do índice.
Fiz as consultas algumas vezes para preencher o cache e selecionei a melhor dentre dez execuções para obter resultados comparáveis.
1. Consulta usando as duas colunas
SELECT *
FROM adratt
WHERE att_id = 90
AND adr_id = 10;
adratt_id | adr_id | att_id | log_up
-----------+--------+--------+---------------------
123 | 10 | 90 | 2008-07-29 09:35:54
(1 row)
Saída de EXPLAIN ANALYZE
:
Index Scan using adratt_uni on adratt (cost=0.00..3.48 rows=1 width=20) (actual time=0.022..0.025 rows=1 loops=1)
Index Cond: ((adr_id = 10) AND (att_id = 90))
Total runtime: 0.067 ms
2. Consulta usando a primeira coluna
SELECT * FROM adratt WHERE adr_id = 10
adratt_id | adr_id | att_id | log_up
-----------+--------+--------+---------------------
126 | 10 | 10 | 2008-07-29 09:35:54
125 | 10 | 13 | 2008-07-29 09:35:54
4711 | 10 | 21 | 2008-07-29 09:35:54
29322 | 10 | 22 | 2011-06-06 15:50:38
29321 | 10 | 30 | 2011-06-06 15:47:17
124 | 10 | 62 | 2008-07-29 09:35:54
21913 | 10 | 78 | 2008-07-29 09:35:54
123 | 10 | 90 | 2008-07-29 09:35:54
28352 | 10 | 106 | 2010-11-22 12:37:50
(9 rows)
Saída de EXPLAIN ANALYZE
:
Index Scan using adratt_uni on adratt (cost=0.00..8.23 rows=9 width=20) (actual time=0.007..0.023 rows=9 loops=1)
Index Cond: (adr_id = 10)
Total runtime: 0.058 ms
3. Consulta usando a segunda coluna
SELECT * FROM adratt WHERE att_id = 90
adratt_id | adr_id | att_id | log_up
-----------+--------+--------+---------------------
123 | 10 | 90 | 2008-07-29 09:35:54
180 | 39 | 90 | 2008-08-29 15:46:07
...
(83 rows)
Saída de EXPLAIN ANALYZE
:
Index Scan using adratt_uni on adratt (cost=0.00..818.51 rows=83 width=20) (actual time=0.014..0.694 rows=83 loops=1)
Index Cond: (att_id = 90)
Total runtime: 0.849 ms
4. Desative o indexscan e o bitmapscan
SET enable_indexscan = off;
SELECT * FROM adratt WHERE att_id = 90
Saída de EXPLAIN ANALYZE:
Bitmap Heap Scan on adratt (cost=779.94..854.74 rows=83 width=20) (actual time=0.558..0.743 rows=83 loops=1)
Recheck Cond: (att_id = 90)
-> Bitmap Index Scan on adratt_uni (cost=0.00..779.86 rows=83 width=0) (actual time=0.544..0.544 rows=83 loops=1)
Index Cond: (att_id = 90)
Total runtime: 0.894 ms
SET enable_bitmapscan = off
SELECT * FROM adratt WHERE att_id = 90
Saída de EXPLAIN ANALYZE
:
Seq Scan on adratt (cost=0.00..1323.10 rows=83 width=20) (actual time=0.009..2.429 rows=83 loops=1)
Filter: (att_id = 90)
Total runtime: 2.680 ms
Conclusão
Como esperado, o índice de várias colunas é usado para uma consulta somente na segunda coluna.
Como esperado, é menos eficaz, mas a consulta ainda é 3x mais rápida do que sem o índice.
Depois de desativar as varreduras de índice, o planejador de consultas escolhe uma varredura de heap de bitmap, que executa quase tão rapidamente. Somente depois de desativar isso, ele também volta a uma varredura seqüencial.
vacuum full
e areindex
. Outros, que ele vai ajudar varreduras de índice na primeira ou ambas as colunas principais muito , mas machucar consultas na segunda coluna. Em uma tabela recém-agrupada em cluster, as linhas com o mesmo valor na segunda coluna são espalhadas, para que um máximo de blocos precise ser lido.re 1) Sim e não.
Para uma consulta que usa as duas 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. Portanto,where 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 gentilmente nos mostrou uma configuração onde funciona), parece que isso depende muito da distribuição de dados da segunda coluna, embora 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 por si mesmo com muita facilidade, basta criar uma chave primária)Eu recomendo que você leia o capítulo sobre índices no manual , que basicamente responde a todas as perguntas acima.
Além disso, qual índice criar? by depesz faz um bom trabalho ao explicar a ordem nas colunas do índice e outros tópicos relacionados ao índice.
fonte
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 nas colunas principais. Isso foi aprimorado na versão 8.1. O manual atual do Postgres 10 (atualizado) explica:
Ênfase minha. Eu posso confirmar isso por experiência própria.
Veja também o caso de teste adicionado à minha resposta posterior aqui .
fonte
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, todas as tuplas no conjunto de resultados devem ser visitadas para verificar a visibilidade. Você pode estar pensando no Oracle.
Os desenvolvedores do PostgreSQL falam sobre "verificações apenas de índice" . De fato, o recurso foi lançado com o Postgres 9.2. Leia a mensagem de confirmação .
Depesz escreveu uma postagem no blog muito informativa .
Índices de cobertura verdadeiros (atualização) são introduzidos com a
INCLUDE
cláusula no Postgres 11. Relacionados:Isso também é um pouco errado:
Conforme relatado nos comentários da minha outra resposta, também executei testes com uma tabela de dois números inteiros e nada mais. O índice mantém as mesmas colunas da tabela. O tamanho de um índice btree é cerca de 2/3 do da tabela. Não é o suficiente para explicar uma aceleração do fator 3. Fiz mais testes, com base na sua configuração, simplificados para duas colunas e com 100000 linhas. Na minha instalação do PostgreSQL 9.0, os resultados foram consistentes.
Se a tabela tiver colunas adicionais, a aceleração do índice se tornará mais substancial, mas esse certamente não é o único fator aqui .
Para resumir os principais pontos:
Os índices de várias colunas podem ser usados com consultas em colunas não principais, mas a aceleração é apenas em torno do fator 3 para critérios seletivos (pequena porcentagem de linhas no resultado). Maior para tuplas maiores, menor para partes maiores da tabela no conjunto de resultados.
Crie um índice adicional nessas colunas se o desempenho for importante.
Se todas as colunas envolvidas forem incluídas em um índice (índice de cobertura) e todas as linhas envolvidas (por bloco) estiverem visíveis para todas as transações, você poderá obter uma "varredura apenas de índice" na página 9.2 ou posterior.
fonte
Eles não são equivalentes e, de um modo geral, o índice (bar, baz) não será eficiente para consultas no 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 em 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 que uma 'verificação 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 principais, 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
As duas maneiras pelas quais o índice pode ajudar são se uma varredura completa do índice for significativamente mais barata que uma varredura completa da tabela e: 1. as pesquisas da tabela forem baratas (porque existem poucas ou estão agrupadas),
ou 2. o índice está cobrindo para que não haja pesquisas de tabela em todos osaspectos, consulte os comentários de Erwins aquitestbed:
consulta 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 via índice é duas vezes mais rápido, atingindo 30 buffers - que em termos de indexação são 'um pouco mais rápidos'! 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 na tabela
Por outro lado, as consultas na coluna principal fazem uso da estrutura btree do índice - nesse caso, atingindo 2 buffers :
fonte