Índice e desempenho de várias colunas

31

Eu tenho uma tabela com um índice de várias colunas e tenho dúvidas sobre a classificação adequada dos índices para obter o desempenho máximo nas consultas.

O cenário:

  • PostgreSQL 8.4, tabela com cerca de um milhão de linhas

  • Os valores na coluna c1 podem ter cerca de 100 valores diferentes . Podemos assumir que os valores estão distribuídos igualmente, portanto, temos cerca de 10000 linhas para cada valor possível.

  • A coluna c2 pode ter 1000 valores diferentes . Temos 1000 linhas para todos os valores possíveis.

Ao pesquisar dados, a condição sempre inclui valores para essas duas colunas; portanto, a tabela possui um índice de várias colunas combinando c1 e c2. Eu li sobre a importância de ordenar corretamente as colunas em um índice de várias colunas se você tiver consultas usando apenas uma coluna para filtragem. Este não é o caso em nosso cenário.

Minha pergunta é esta:

Dado o fato de um dos filtros selecionar um conjunto de dados muito menor, eu poderia melhorar o desempenho se o primeiro índice for o mais seletivo (aquele que permite um conjunto menor)? Eu nunca tinha considerado essa pergunta até ver os gráficos do artigo mencionado:

insira a descrição da imagem aqui

Imagem retirada do artigo referenciado sobre índices de várias colunas .

As consultas usam valores das duas colunas para filtragem. Não tenho consultas usando apenas uma coluna para filtragem. Todos eles são: WHERE c1=@ParameterA AND c2=@ParameterB. Também existem condições como esta:WHERE c1 = "abc" AND c2 LIKE "ab%"

jap1968
fonte

Respostas:

36

Responda

Como você se refere ao site use-the-index-luke.com, considere o capítulo:

Use o Índice, Lucas ›A Cláusula Where› Procurando por Intervalos › Maior, Menos e ENTRE

Ele tem um exemplo que combina perfeitamente com a sua situação (índice de duas colunas, um é testado quanto à igualdade e o outro ao intervalo ), explica (com mais desses bons gráficos de índice) por que o conselho do @ ypercube é preciso e resume:

Rule of thumb: index for equality first  then for ranges.

Também é bom para apenas uma coluna?

O que fazer para consultas em apenas uma coluna parece estar claro. Mais detalhes e parâmetros de referência a respeito dessa pergunta relacionada:

Coluna menos seletiva primeiro?

Além disso, e se você tiver apenas condições de igualdade para as duas colunas ?

Isso não importa . Coloque a coluna em primeiro lugar, com maior probabilidade de receber condições próprias, o que realmente importa.

Considere esta demonstração ou reproduza-a você mesmo. Eu crio uma tabela simples de duas colunas com 100 mil linhas. Um com muito poucos , o outro com muitos valores distintos:

CREATE TEMP TABLE t AS
SELECT (random() * 10000)::int AS lots
     , (random() * 4)::int     AS few
FROM generate_series (1, 100000);

DELETE FROM t WHERE random() > 0.9;  -- create some dead tuples, more "real-life"

ANALYZE t;

SELECT count(distinct lots)   -- 9999
     , count(distinct few)    --    5
FROM   t;

Inquerir:

SELECT *
FROM   t
WHERE  lots = 2345
AND    few = 2;

EXPLAIN ANALYZE saída (o melhor de 10 para excluir efeitos de armazenamento em cache):

Varredura Seq em t (custo = 0,00..5840,84 linhas = 2 largura = 8)
               (tempo real = 5.646..15.535 linhas = 2 loops = 1)
  Filtro: ((lotes = 2345) AND (poucos = 2))
  Buffers: golpe local = 443
Tempo de execução total: 15.557 ms

Adicionar índice, testar novamente:

CREATE INDEX t_lf_idx ON t(lots, few);
Varredura de índice usando t_lf_idx em t (custo = 0.00..3.76 linhas = 2 largura = 8)
                                (tempo real = 0,008..0,011 linhas = 2 loops = 1)
  Índice Cond: ((lotes = 2345) AND (poucos = 2))
  Buffers: golpe local = 4
Tempo de execução total: 0,027 ms

Adicione outro índice, teste novamente:

DROP INDEX t_lf_idx;
CREATE INDEX t_fl_idx  ON t(few, lots);
Varredura de índice usando t_fl_idx em t (custo = 0,00..3,74 linhas = 2 largura = 8)
                                (tempo real = 0,007..0,011 linhas = 2 loops = 1)
  Índice Cond: ((poucos = 2) AND (lotes = 2345))
  Buffers: golpe local = 4
Tempo de execução total: 0,027 ms
Erwin Brandstetter
fonte
Este também é o caso de 3 (ou mais) colunas no índice?
hayd 19/09
@ Hayd: Não sei ao que "isso" se refere. Você pode fazer uma nova pergunta . Você sempre pode fazer referência a este para contexto. (E envie um comentário aqui para o link de volta.)
Erwin Brandstetter
Por "isto", quero dizer ", a ordenação da definição do índice é importante se houver mais de 2 colunas na definição do índice"
hayd 19/09/09
@ hayd: Ponto mais importante: um índice btree é bom para consultas com condições de igualdade nas principais expressões de índice. A ordem entre eles é principalmente irrelevante. Muitos outros detalhes que não cabem em um comentário ...
Erwin Brandstetter 19/09
Obrigado, vou tentar escrever uma pergunta coerente e vincular a ela.
hayd 20/09
11

Se, como você diz, as consultas que envolvem essas 2 colunas, são todas verificações de igualdade de ambas as colunas, por exemplo:

WHERE c1=@ParameterA AND c2=@ParameterB

não se preocupe com isso. Duvido que haja alguma diferença e, se houver, será insignificante. Você sempre pode testar, é claro, com seus dados e configurações do servidor. Versões diferentes de um DBMS podem se comportar de maneira ligeiramente diferente em relação à otimização.

A ordem dentro do índice importaria para outros tipos de consultas, com verificações de apenas uma coluna ou condições de desigualdade ou condições em uma coluna e agrupamento na outra, etc.

Se eu escolhesse uma das duas ordens, colocaria a coluna menos seletiva em primeiro lugar. Considere uma tabela com colunas yeare month. É mais provável que você precise de uma WHERE year = 2000condição ou a WHERE year BETWEEN 2000 AND 2013ou a WHERE (year, month) BETWEEN (1999, 6) AND (2000, 5).

Uma consulta do tipo WHERE month = 7 GROUP BY yearpode ter certeza (encontrar pessoas nascidas em julho), mas seria menos frequente. Isso depende, é claro, dos dados reais armazenados na sua tabela. Escolha um pedido por enquanto, diga o (c1, c2)e você sempre poderá adicionar outro índice posteriormente (c2, c1).


Atualização, após o comentário do OP:

Também existem condições como esta: WHERE c1 = 'abc' AND c2 LIKE 'ab%'

Esse tipo de consulta se exatamente uma condição de intervalo na c2coluna e precisaria de um (c1, c2)índice. Se você também tiver consultas do tipo reverso:

WHERE c2 = 'abc' AND c1 LIKE 'ab%'

seria bom se você também tivesse um (c2, c1)índice.

ypercubeᵀᴹ
fonte