Índices compostos: coluna mais seletiva primeiro?


17

Eu tenho lido composite indexese estou um pouco confuso sobre pedidos. Esta documentação (pouco menos da metade do caminho) diz

Em geral, você deve colocar a coluna que se espera que seja usada com mais frequência primeiro no índice.

No entanto, logo após ele diz

crie um índice composto colocando a coluna mais seletiva em primeiro lugar; isto é, a coluna com mais valores.

A Oracle também diz aqui em outras palavras

Se todas as chaves forem usadas nas cláusulas WHERE com a mesma frequência, ordenar essas chaves da mais seletiva para a menos seletiva na instrução CREATE INDEX melhorará o desempenho da consulta.

No entanto, encontrei uma resposta SO que diz diferentemente. Diz

Organize as colunas com a coluna menos seletiva primeiro e a coluna mais seletiva por último. No caso de uma ligação de empate com a coluna que é mais provável de ser usada sozinha.

A primeira documentação que referenciei diz que você deve primeiro usar os mais usados, enquanto a resposta do SO diz que isso deve ser apenas para desempate. Então eles também diferem na ordem.

Esta documentação também fala skip scanninge diz

A varredura de ignorar é vantajosa se houver poucos valores distintos na coluna inicial do índice composto e muitos valores distintos na chave não-líder do índice.

Outro artigo diz

A coluna de prefixo deve ser a mais discriminante e a mais usada nas consultas

que eu acredito que mais discriminatório significaria mais distinto.

Toda essa pesquisa ainda me leva à mesma pergunta; A coluna mais seletiva deve ser a primeira ou a última? A primeira coluna deve ser a mais usada e a mais seletiva em um tie-break?

Esses artigos parecem estar se contradizendo, mas oferecem alguns exemplos. Pelo que reuni, parece ser mais eficiente least selective columnque seja o primeiro a ordenar se você está antecipando Index Skip Scans. Mas não tenho muita certeza se isso está correto.


Respostas:


8

De AskTom

(no 9i, há uma nova "index skip scan" - procure por lá para ler sobre isso. Torna o índice (a, b) OR (b, a) útil nos dois casos acima algumas vezes!)

Portanto, a ordem das colunas no seu índice depende de como suas consultas são gravadas. Você deseja usar o índice para o maior número possível de consultas (para reduzir o número total de índices que você possui) - isso orientará a ordem das colunas. Nada mais (a seletividade de a ou b não conta).

Um dos argumentos para organizar colunas no índice composto na ordem dos menos discriminantes (valores menos distintos) para os mais discriminantes (valores mais distintos) é para a compactação da chave de índice.

SQL> create table t as select * from all_objects;

Table created.

SQL> create index t_idx_1 on t(owner,object_type,object_name);

Index created.

SQL> create index t_idx_2 on t(object_name,object_type,owner);

Index created.

SQL> select count(distinct owner), count(distinct object_type), count(distinct object_name ), count(*)  from t;

COUNT(DISTINCTOWNER) COUNT(DISTINCTOBJECT_TYPE) COUNT(DISTINCTOBJECT_NAME)      COUNT(*)
-------------------- -------------------------- --------------------------      ----------
                 30                         45                       52205      89807

SQL> analyze index t_idx_1 validate structure; 

Index analyzed.

SQL> select btree_space, pct_used, opt_cmpr_count, opt_cmpr_pctsave from index_stats;

BTREE_SPACE   PCT_USED OPT_CMPR_COUNT OPT_CMPR_PCTSAVE
----------- ---------- -------------- ----------------
    5085584     90          2           28

SQL> analyze index t_idx_2 validate structure; 

Index analyzed.

SQL> select btree_space, pct_used, opt_cmpr_count, opt_cmpr_pctsave  from index_stats; 

BTREE_SPACE   PCT_USED OPT_CMPR_COUNT OPT_CMPR_PCTSAVE
----------- ---------- -------------- ----------------
    5085584     90          1           14

De acordo com as estatísticas do índice, o primeiro índice é mais compressível.

Outra é como o índice é usado em suas consultas. Se suas consultas usam principalmentecol1 ,

Por exemplo, se você tiver consultas como

  • select * from t where col1 = :a and col2 = :b;
  • select * from t where col1 = :a;

    -então index(col1,col2) teria um desempenho melhor.

    Se suas consultas costumam ser usadas col2,

  • select * from t where col1 = :a and col2 = :b;
  • select * from t where col2 = :b;

    -então index(col2,col1) teria um desempenho melhor. Se todas as suas consultas sempre especificarem ambas as colunas, não importa qual coluna será a primeira no índice composto.

    Em conclusão, as principais considerações na ordenação de colunas do índice composto são a compactação da chave de índice e como você usará esse índice em suas consultas.

    Referências:

  • Ordem das colunas no Índice
  • É menos eficiente ter colunas com baixa cardinalidade em um índice (à direita)?
  • Index Skip Scan - A ordem da coluna do índice importa mais? (Sinal de aviso)


  • 3

    O mais seletivo primeiro é útil apenas quando esta coluna está na cláusula WHERE real.

    Quando o SELECT é de um grupo maior (menos seletivo) e, possivelmente, de outros valores não indexados, um índice com colunas menos seletivas ainda pode ser útil (se houver um motivo para não criar outro).

    Se houver uma tabela ADDRESS, com

    COUNTRY CITY STREET, outra coisa ...

    a indexação STREET, CITY, COUNTRY produzirá as consultas mais rápidas com o nome de uma rua. Mas, consultando todas as ruas de uma cidade, o índice será inútil e a consulta provavelmente fará uma verificação completa da tabela.

    A indexação de PAÍS, CIDADE, RUA pode ser um pouco mais lenta para ruas individuais, mas o índice pode ser usado para outras consultas, selecionando apenas por país e / ou cidade.


    3

    Ao escolher a ordem da coluna do índice, a principal preocupação é:

    Existem predicados (igualdade) nessa coluna nas minhas consultas?

    Se uma coluna nunca aparecer em uma cláusula where, não vale a pena indexar (1)

    OK, então você tem uma tabela e consultas em cada coluna. Às vezes mais de um.

    Como você decide o que indexar?

    Vejamos um exemplo. Aqui está uma tabela com três colunas. Um contém 10 valores, outros 1.000, os últimos 10.000:

    create table t(
      few_vals  varchar2(10),
      many_vals varchar2(10),
      lots_vals varchar2(10)
    );
    
    insert into t 
    with rws as (
      select lpad(mod(rownum, 10), 10, '0'), 
             lpad(mod(rownum, 1000), 10, '0'), 
             lpad(rownum, 10, '0') 
      from dual connect by level <= 10000
    )
      select * from rws;
    
    commit;
    
    select count(distinct few_vals),
           count(distinct many_vals) ,
           count(distinct lots_vals) 
    from   t;
    
    COUNT(DISTINCTFEW_VALS)  COUNT(DISTINCTMANY_VALS)  COUNT(DISTINCTLOTS_VALS)  
    10                       1,000                     10,000     

    Estes são números deixados com zeros. Isso ajudará a esclarecer mais tarde a compactação.

    Então você tem três consultas comuns:

    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  few_vals = '0000000001';
    
    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001';
    
    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001'
    and    few_vals = '0000000001';

    O que você indexa?

    Um índice com apenas alguns valores é apenas marginalmente melhor do que uma verificação completa da tabela:

    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  few_vals = '0000000001';
    
    select * 
    from table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    -------------------------------------------------------------------------------------------  
    | Id  | Operation            | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  
    -------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT     |          |      1 |        |      1 |00:00:00.01 |      61 |  
    |   1 |  SORT AGGREGATE      |          |      1 |      1 |      1 |00:00:00.01 |      61 |  
    |   2 |   VIEW               | VW_DAG_0 |      1 |   1000 |   1000 |00:00:00.01 |      61 |  
    |   3 |    HASH GROUP BY     |          |      1 |   1000 |   1000 |00:00:00.01 |      61 |  
    |   4 |     TABLE ACCESS FULL| T        |      1 |   1000 |   1000 |00:00:00.01 |      61 |  
    -------------------------------------------------------------------------------------------
    
    select /*+ index (t (few_vals)) */
           count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  few_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    -------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  
    -------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |      58 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |      58 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |   1000 |   1000 |00:00:00.01 |      58 |  
    |   3 |    HASH GROUP BY                       |          |      1 |   1000 |   1000 |00:00:00.01 |      58 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |   1000 |   1000 |00:00:00.01 |      58 |  
    |   5 |      INDEX RANGE SCAN                  | FEW      |      1 |   1000 |   1000 |00:00:00.01 |       5 |  
    -------------------------------------------------------------------------------------------------------------

    Portanto, é improvável que valha a pena indexar por conta própria. As consultas em lots_vals retornam algumas linhas (apenas 1 neste caso). Portanto, isso definitivamente vale a pena indexar.

    Mas e as consultas nas duas colunas?

    Você deve indexar:

    ( few_vals, lots_vals )

    OU

    ( lots_vals, few_vals )

    Truque de pergunta!

    A resposta é nenhuma.

    Certamente few_vals é uma string longa. Assim, você pode obter uma boa compactação. E você (pode) obter uma verificação de índice para as consultas usando (poucos_valos, lotes_valores) que possuem apenas predicados em lotes_valores. Mas não estou aqui, mesmo que ele tenha um desempenho notavelmente melhor que uma verificação completa:

    create index few_lots on t(few_vals, lots_vals);
    
    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    -------------------------------------------------------------------------------------------  
    | Id  | Operation            | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  
    -------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT     |          |      1 |        |      1 |00:00:00.01 |      61 |  
    |   1 |  SORT AGGREGATE      |          |      1 |      1 |      1 |00:00:00.01 |      61 |  
    |   2 |   VIEW               | VW_DAG_0 |      1 |      1 |      1 |00:00:00.01 |      61 |  
    |   3 |    HASH GROUP BY     |          |      1 |      1 |      1 |00:00:00.01 |      61 |  
    |   4 |     TABLE ACCESS FULL| T        |      1 |      1 |      1 |00:00:00.01 |      61 |  
    -------------------------------------------------------------------------------------------  
    
    select /*+ index_ss (t few_lots) */count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    ----------------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |  
    ----------------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |      13 |     11 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |      13 |     11 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |      1 |      1 |00:00:00.01 |      13 |     11 |  
    |   3 |    HASH GROUP BY                       |          |      1 |      1 |      1 |00:00:00.01 |      13 |     11 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |      1 |      1 |00:00:00.01 |      13 |     11 |  
    |   5 |      INDEX SKIP SCAN                   | FEW_LOTS |      1 |     40 |      1 |00:00:00.01 |      12 |     11 |  
    ----------------------------------------------------------------------------------------------------------------------

    Você gosta de jogar? 2)

    Então você ainda precisa de um índice com lotes_valos como a coluna principal. E, pelo menos nesse caso, o índice composto (poucos, lotes) realiza a mesma quantidade de trabalho que um em apenas (lotes)

    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001'
    and    few_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    -------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  
    -------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |       3 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |       3 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |      1 |      1 |00:00:00.01 |       3 |  
    |   3 |    HASH GROUP BY                       |          |      1 |      1 |      1 |00:00:00.01 |       3 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |      1 |      1 |00:00:00.01 |       3 |  
    |   5 |      INDEX RANGE SCAN                  | FEW_LOTS |      1 |      1 |      1 |00:00:00.01 |       2 |  
    -------------------------------------------------------------------------------------------------------------  
    
    create index lots on t(lots_vals);
    
    select /*+ index (t (lots_vals)) */count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001'
    and    few_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    ----------------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |  
    ----------------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |       3 |      1 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |       3 |      1 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |      1 |      1 |00:00:00.01 |       3 |      1 |  
    |   3 |    HASH GROUP BY                       |          |      1 |      1 |      1 |00:00:00.01 |       3 |      1 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |      1 |      1 |00:00:00.01 |       3 |      1 |  
    |   5 |      INDEX RANGE SCAN                  | LOTS     |      1 |      1 |      1 |00:00:00.01 |       2 |      1 |  
    ----------------------------------------------------------------------------------------------------------------------  

    Haverá casos em que o índice composto economiza 1-2 IOs. Mas vale a pena ter dois índices para essa economia?

    E há outro problema com o índice composto. Compare o fator de cluster para os três índices, incluindo LOTS_VALS:

    create index lots on t(lots_vals);
    create index lots_few on t(lots_vals, few_vals);
    create index few_lots on t(few_vals, lots_vals);
    
    select index_name, leaf_blocks, distinct_keys, clustering_factor
    from   user_indexes
    where  table_name = 'T';
    
    INDEX_NAME  LEAF_BLOCKS  DISTINCT_KEYS  CLUSTERING_FACTOR  
    FEW_LOTS    47           10,000         530                
    LOTS_FEW    47           10,000         53                 
    LOTS        31           10,000         53                 
    FEW         31           10             530    

    Observe que o fator de agrupamento para poucos_ lotes é 10x maior que para lotes e lotes_fundos! E isso está em uma tabela de demonstração com cluster perfeito para começar. Nos bancos de dados do mundo real, o efeito provavelmente será pior.

    Então, o que há de tão ruim nisso?

    O fator de agrupamento é um dos principais fatores que determinam o quão "atraente" é um índice. Quanto maior, menor a probabilidade de o otimizador escolher. Especialmente se os lotes_vals não forem realmente únicos, mas ainda assim normalmente tiverem poucas linhas por valor. Se você tiver azar, isso pode ser suficiente para fazer o otimizador pensar que uma verificação completa é mais barata ...

    OK, portanto, índices compostos com poucos valores e muitos valores têm apenas benefícios de caso de borda.

    E as consultas que filtram alguns valores e muitos valores?

    Os índices de colunas únicas oferecem apenas pequenos benefícios. Mas combinados, eles retornam poucos valores. Portanto, um índice composto é uma boa ideia. Mas qual o caminho?

    Se você colocar alguns em primeiro lugar, compactar a coluna principal tornará essa menor

    create index few_many on t(many_vals, few_vals);
    create index many_few on t(few_vals, many_vals);
    
    select index_name, leaf_blocks, distinct_keys, clustering_factor 
    from   user_indexes
    where  index_name in ('FEW_MANY', 'MANY_FEW');
    
    INDEX_NAME  LEAF_BLOCKS  DISTINCT_KEYS  CLUSTERING_FACTOR  
    FEW_MANY    47           1,000          10,000             
    MANY_FEW    47           1,000          10,000   
    
    alter index few_many rebuild compress 1;
    alter index many_few rebuild compress 1;
    
    select index_name, leaf_blocks, distinct_keys, clustering_factor 
    from   user_indexes
    where  index_name in ('FEW_MANY', 'MANY_FEW');
    
    INDEX_NAME  LEAF_BLOCKS  DISTINCT_KEYS  CLUSTERING_FACTOR  
    MANY_FEW    31           1,000          10,000             
    FEW_MANY    34           1,000          10,000      

    Com menos valores diferentes na coluna principal, compacta melhor. Portanto, há um pouco menos de trabalho para ler este índice. Mas apenas um pouco. E ambos já são um bom pedaço menor que o original (redução de 25% no tamanho).

    E você pode ir além e comprimir todo o índice!

    alter index few_many rebuild compress 2;
    alter index many_few rebuild compress 2;
    
    select index_name, leaf_blocks, distinct_keys, clustering_factor 
    from   user_indexes
    where  index_name in ('FEW_MANY', 'MANY_FEW');
    
    INDEX_NAME  LEAF_BLOCKS  DISTINCT_KEYS  CLUSTERING_FACTOR  
    FEW_MANY    20           1,000          10,000             
    MANY_FEW    20           1,000          10,000   

    Agora os dois índices estão de volta ao mesmo tamanho. Observe que isso tira proveito do fato de que há uma relação entre poucos e muitos. Novamente, é improvável que você veja esse tipo de benefício no mundo real.

    Até agora, falamos apenas sobre verificações de igualdade. Geralmente, com índices compostos, você terá uma desigualdade em relação a uma das colunas. por exemplo, consultas como "obter pedidos / remessas / faturas para um cliente nos últimos N dias".

    Se você possui esses tipos de consultas, deseja a igualdade na primeira coluna do índice:

    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  few_vals < '0000000002'
    and    many_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    -------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  
    -------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |      12 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |      12 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |     10 |     10 |00:00:00.01 |      12 |  
    |   3 |    HASH GROUP BY                       |          |      1 |     10 |     10 |00:00:00.01 |      12 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |     10 |     10 |00:00:00.01 |      12 |  
    |   5 |      INDEX RANGE SCAN                  | FEW_MANY |      1 |     10 |     10 |00:00:00.01 |       2 |  
    -------------------------------------------------------------------------------------------------------------  
    
    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  few_vals = '0000000001'
    and    many_vals < '0000000002';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    ----------------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |  
    ----------------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |      12 |      1 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |      12 |      1 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |      2 |     10 |00:00:00.01 |      12 |      1 |  
    |   3 |    HASH GROUP BY                       |          |      1 |      2 |     10 |00:00:00.01 |      12 |      1 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |      2 |     10 |00:00:00.01 |      12 |      1 |  
    |   5 |      INDEX RANGE SCAN                  | MANY_FEW |      1 |      1 |     10 |00:00:00.01 |       2 |      1 |  
    ----------------------------------------------------------------------------------------------------------------------  

    Observe que eles estão usando o índice oposto.

    TL; DR

    • As colunas com condições de igualdade devem ir primeiro no índice.
    • Se você tiver várias colunas com igualdade na sua consulta, colocar primeiro a que tiver menos valores diferentes fornecerá a melhor vantagem de compactação
    • Embora as pular verificações de índice sejam possíveis, você precisa ter certeza de que isso continuará sendo uma opção viável no futuro próximo
    • Índices compostos, incluindo colunas quase únicas, oferecem benefícios mínimos. Certifique-se de que realmente precisa salvar os 1-2 IOs

    1: Em alguns casos, pode valer a pena incluir uma coluna em um índice, se isso significa que todas as colunas da sua consulta estão no índice. Isso permite uma verificação apenas do índice, para que você não precise acessar a tabela.

    2: se você estiver licenciado para Diagnóstico e Ajuste, poderá forçar o plano a ignorar a varredura com o SQL Plan Management

    ADDEDNDA

    PS - os documentos que você citou são do 9i. Isso é realmente velho. Eu ficaria com algo mais recente


    Uma consulta é select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )realmente comum? O Oracle não permite a sintaxe select count (distinct few_vals, many_vals, lots_vals )- que não faz concatenação de string, não precisa que as colunas sejam do tipo texto e não depende da ausência de :caractere?
    precisa saber é o seguinte

    @ ypercubeᵀᴹ você não pode fazer um count ( distinct x, y, z )no Oracle. Portanto, você precisa fazer uma subconsulta distinta e contar os resultados ou uma concatenação como a acima. Eu só fiz isso aqui para forçar um acesso à tabela (em vez de indexar apenas scan) e só tem uma linha no resultado
    Chris Saxon

    1

    Há mais elementos de consulta que contribuem para a decisão final sobre o que um Índice Composto deve iniciar e / ou conter além da seletividade da coluna.

    por exemplo:

    1. que tipo de operador de consulta está sendo usado: se as consultas tiverem operadores como
      ">,> =, <, <="
    2. Quantas linhas reais são esperadas como resultado da consulta: O resultado da consulta será a maioria das linhas da tabela.
    3. Há alguma função sendo usada na coluna da tabela durante a cláusula Where: Se a consulta tiver alguma função UPPER, LOWER, TRIM, SUBSTRING usada na coluna que está sendo usada na condição WHERE.

    para manter a conversa relevante, minha resposta abaixo se aplica à seguinte situação:

    1. "90% do tipo de consultas em uma determinada tabela possui a cláusula WHERE com operator ="
    2. "no máximo, a consulta está retornando 10% do total de linhas na tabela como resultado"
    3. "nenhum tipo de função está sendo usado na coluna da tabela na cláusula WHERE"
    4. "na maioria das vezes as colunas da cláusula WHERE usadas são principalmente do tipo número,
      sequência"

    Na minha experiência, é sobre isso que o DBA deve estar atento.

    Vamos imaginar que a única regra está sendo aplicada:

    1) Se eu criar um índice com a coluna mais seletiva sendo a primeira, mas essa coluna não for realmente usada pela maioria das consultas nessa tabela, será inútil para o mecanismo db.

    2) Se eu criar um índice com a coluna mais usada em uma consulta, sendo a primeira no índice, mas a coluna tiver baixa seletividade, o desempenho da minha consulta também não será bom.

    Vou listar as colunas usadas principalmente em 90% das consultas da tabela. Em seguida, coloque aqueles somente da ordem da maior cardinalidade à menor cardinalidade.

    Usamos índices para melhorar o desempenho da consulta de leitura e esse fluxo de trabalho (tipos de consulta de leitura) deve apenas conduzir a criação do índice. De fato, à medida que os dados aumentam (bilhões de linhas), o índice compactado pode economizar armazenamento, mas com certeza prejudica o desempenho da consulta de leitura.


    1

    Em teoria, a coluna mais seletiva produz a pesquisa mais rápida. Mas, no trabalho, acabei encontrando uma situação em que temos um índice composto de 3 partes com a parte mais seletiva primeiro. (data, autor, editora, digamos, nessa ordem, a tabela monitora as postagens) e eu tenho uma consulta que usa todas as três partes. O Mysql usa como padrão o índice author onlny, ignorando o índice composto que contém a empresa e a data, apesar de eles estarem presentes na minha consulta. Usei o índice de força para usar o composto e a consulta realmente ficou mais lenta. Por que isso aconteceu? Eu direi a você:

    Eu estava selecionando um intervalo na data, portanto, apesar da data ser altamente seletiva, o fato de estarmos usando-o para varreduras de intervalo (mesmo que o intervalo seja relativamente curto, 6 meses em 6 anos de dados) tornaram o composto prejudicial para mysql. Para usar o composto nesse caso em particular, o mysql precisa capturar todos os artigos escritos desde o ano novo e depois descobrir quem é o autor e, como o autor não escreveu tantos artigos em comparação com outros autores, o mysql preferiu apenas encontrar esse autor. .

    Em outro caso, a consulta foi muito mais rápida no composto, quando um autor era muito popular e possuía a maioria dos registros, classificando por data. Mas o mysql não detectou automaticamente esse caso, eu tive que forçar o índice ... Então, você sabe, isso varia. As varreduras de intervalo podem tornar sua coluna seletiva inútil. A distribuição dos dados pode gerar casos em que as colunas são mais seletivas para diferentes registros ...

    O que eu faria de diferente é mudar a data (que, novamente, em teoria, é a mais seletiva) para a direita, pois sei que vou fazer uma varredura de faixa agora e isso faz a diferença.


    11
    Se a consulta tinha algo como WHERE (date BETWEEN @x AND @y) AND (author = @a) AND (publishing company = @p), em seguida, um índice em (author, publishing_company, date)ou (publishing_company, author, date)seria melhor e seria usada - sem forçar.
    ypercubeᵀᴹ

    -2

    Casos diferentes para diferentes situações. Conheça seu objetivo; crie seus índices e execute planos de explicação para cada um e você terá sua melhor resposta para sua situação.


    -2

    Da ordem da coluna no Índice em Ask Tom:

    Portanto, a ordem das colunas no seu índice depende de como suas consultas são gravadas. Você deseja usar o índice para o maior número possível de consultas (para reduzir o número total de índices que você possui) - isso orientará a ordem das colunas. Nada mais (a seletividade de a ou b não conta).

    Concordo que precisamos ordenar colunas com base na cláusula where, mas a declaração "(a seletividade de a ou b não conta)" não está correta.) ". As colunas mais seletivas devem liderar se a primeira função for satisfeita ("cláusula where")

    Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
    Licensed under cc by-sa 3.0 with attribution required.