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 / 28592
Accepted
eggyal
eggyal
Asked: 2012-11-14 00:42:19 +0800 CST2012-11-14 00:42:19 +0800 CST 2012-11-14 00:42:19 +0800 CST

Um índice deve abranger todas as colunas selecionadas para que seja usado para ORDER BY?

  • 772

No SO, alguém perguntou recentemente Por que o ORDER BY não está usando o índice?

A situação envolvia uma tabela InnoDB simples no MySQL com três colunas e 10 mil linhas. Uma das colunas, um número inteiro, foi indexada - e o OP procurou recuperar toda a tabela classificada nessa coluna:

SELECT * FROM person ORDER BY age

Ele anexou EXPLAINa saída mostrando que essa consulta foi resolvida com um filesort(em vez do índice) e perguntou por que isso aconteceria.

Apesar da dica que faz FORCE INDEX FOR ORDER BY (age) com que o índice seja usado , alguém respondeu (com comentários de apoio/votos positivos de outros) que um índice só é usado para classificação quando todas as colunas selecionadas são lidas do índice (ou seja, como normalmente seria indicado por Using indexna Extracoluna de EXPLAINsaída). Posteriormente, foi dada uma explicação de que percorrer o índice e, em seguida, buscar colunas da tabela resulta em E/S aleatória, que o MySQL considera mais caro do que um arquivo filesort.

Isso parece ir contra o capítulo do manual sobre ORDER BYOtimização , que não apenas transmite a forte impressão de que a satisfação ORDER BYde um índice é preferível a realizar uma classificação adicional (na verdade, filesorté uma combinação de quicksort e mergesort e , portanto, deve ter um limite inferior de ; enquanto percorrer o índice em ordem e procurar na tabela deveria ser - então isso faz todo o sentido), mas também deixa de mencionar essa suposta "otimização" ao mesmo tempo em que afirma:Ω(nlog n)O(n)

As consultas a seguir usam o índice para resolver a ORDER BYpeça:

SELECT * FROM t1
  ORDER BY key_part1,key_part2,... ;

Na minha leitura, esse é precisamente o caso nesta situação (ainda assim o índice não estava sendo usado sem uma dica explícita).

Minhas perguntas são:

  • É realmente necessário que todas as colunas selecionadas sejam indexadas para que o MySQL opte por usar o índice?

    • Em caso afirmativo, onde isso está documentado (se houver)?

    • Se não, o que estava acontecendo aqui?

mysql index
  • 2 2 respostas
  • 6120 Views

2 respostas

  • Voted
  1. RolandoMySQLDBA
    2012-11-14T15:49:40+08:002012-11-14T15:49:40+08:00

    É realmente necessário que todas as colunas selecionadas sejam indexadas para que o MySQL opte por usar o índice?

    Essa é uma pergunta complicada porque existem fatores que determinam se vale a pena usar um índice.

    FATOR Nº 1

    Para qualquer índice, qual é a população-chave? Em outras palavras, qual é a cardinalidade (contagem distinta) de todas as tuplas registradas no índice?

    FATOR Nº 2

    Qual mecanismo de armazenamento você está usando? Todas as colunas necessárias são acessíveis a partir de um índice?

    QUAL É O PRÓXIMO ???

    Vamos dar um exemplo simples: uma tabela que contém dois valores (Masculino e Feminino)

    Vamos criar uma tabela com um teste para uso de índice

    USE test
    DROP TABLE IF EXISTS mf;
    CREATE TABLE mf
    (
        id int not null auto_increment,
        gender char(1),
        primary key (id),
        key (gender)
    ) ENGINE=InnODB;
    INSERT INTO mf (gender) VALUES
    ('M'),('M'),('M'),('M'),('M'),('M'),('M'),('M'),
    ('M'),('M'),('M'),('M'),('F'),('F'),('M'),('M'),
    ('M'),('M'),('M'),('M'),('M'),('M'),('M'),('M'),
    ('M'),('M'),('M'),('M'),('M'),('M'),('M'),('M'),
    ('F'),('M'),('M'),('M'),('M'),('M'),('M'),('M');
    ANALYZE TABLE mf;
    EXPLAIN SELECT gender FROM mf WHERE gender='F';
    EXPLAIN SELECT gender FROM mf WHERE gender='M';
    EXPLAIN SELECT id FROM mf WHERE gender='F';
    EXPLAIN SELECT id FROM mf WHERE gender='M';
    

    TESTE InnoDB

    mysql> USE test
    Database changed
    mysql> DROP TABLE IF EXISTS mf;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> CREATE TABLE mf
        -> (
        ->     id int not null auto_increment,
        ->     gender char(1),
        ->     primary key (id),
        ->     key (gender)
        -> ) ENGINE=InnoDB;
    Query OK, 0 rows affected (0.07 sec)
    
    mysql> INSERT INTO mf (gender) VALUES
        -> ('M'),('M'),('M'),('M'),('M'),('M'),('M'),('M'),
        -> ('M'),('M'),('M'),('M'),('F'),('F'),('M'),('M'),
        -> ('M'),('M'),('M'),('M'),('M'),('M'),('M'),('M'),
        -> ('M'),('M'),('M'),('M'),('M'),('M'),('M'),('M'),
        -> ('F'),('M'),('M'),('M'),('M'),('M'),('M'),('M');
    Query OK, 40 rows affected (0.06 sec)
    Records: 40  Duplicates: 0  Warnings: 0
    
    mysql> ANALYZE TABLE mf;
    +---------+---------+----------+----------+
    | Table   | Op      | Msg_type | Msg_text |
    +---------+---------+----------+----------+
    | test.mf | analyze | status   | OK       |
    +---------+---------+----------+----------+
    1 row in set (0.00 sec)
    
    mysql> EXPLAIN SELECT gender FROM mf WHERE gender='F';
    +----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
    | id | select_type | table | type | possible_keys | key    | key_len | ref   | rows | Extra                    |
    +----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
    |  1 | SIMPLE      | mf    | ref  | gender        | gender | 2       | const |    3 | Using where; Using index |
    +----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
    1 row in set (0.00 sec)
    
    mysql> EXPLAIN SELECT gender FROM mf WHERE gender='M';
    +----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
    | id | select_type | table | type | possible_keys | key    | key_len | ref   | rows | Extra                    |
    +----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
    |  1 | SIMPLE      | mf    | ref  | gender        | gender | 2       | const |   37 | Using where; Using index |
    +----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
    1 row in set (0.00 sec)
    
    mysql> EXPLAIN SELECT id FROM mf WHERE gender='F';
    +----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
    | id | select_type | table | type | possible_keys | key    | key_len | ref   | rows | Extra                    |
    +----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
    |  1 | SIMPLE      | mf    | ref  | gender        | gender | 2       | const |    3 | Using where; Using index |
    +----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
    1 row in set (0.00 sec)
    
    mysql> EXPLAIN SELECT id FROM mf WHERE gender='M';
    +----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
    | id | select_type | table | type | possible_keys | key    | key_len | ref   | rows | Extra                    |
    +----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
    |  1 | SIMPLE      | mf    | ref  | gender        | gender | 2       | const |   37 | Using where; Using index |
    +----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
    1 row in set (0.00 sec)
    
    mysql>
    

    TESTE MyISAM

    mysql> USE test
    Database changed
    mysql> DROP TABLE IF EXISTS mf;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> CREATE TABLE mf
        -> (
        ->     id int not null auto_increment,
        ->     gender char(1),
        ->     primary key (id),
        ->     key (gender)
        -> ) ENGINE=MyISAM;
    Query OK, 0 rows affected (0.05 sec)
    
    mysql> INSERT INTO mf (gender) VALUES
        -> ('M'),('M'),('M'),('M'),('M'),('M'),('M'),('M'),
        -> ('M'),('M'),('M'),('M'),('F'),('F'),('M'),('M'),
        -> ('M'),('M'),('M'),('M'),('M'),('M'),('M'),('M'),
        -> ('M'),('M'),('M'),('M'),('M'),('M'),('M'),('M'),
        -> ('F'),('M'),('M'),('M'),('M'),('M'),('M'),('M');
    Query OK, 40 rows affected (0.00 sec)
    Records: 40  Duplicates: 0  Warnings: 0
    
    mysql> ANALYZE TABLE mf;
    +---------+---------+----------+----------+
    | Table   | Op      | Msg_type | Msg_text |
    +---------+---------+----------+----------+
    | test.mf | analyze | status   | OK       |
    +---------+---------+----------+----------+
    1 row in set (0.00 sec)
    
    mysql> EXPLAIN SELECT gender FROM mf WHERE gender='F';
    +----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
    | id | select_type | table | type | possible_keys | key    | key_len | ref   | rows | Extra                    |
    +----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
    |  1 | SIMPLE      | mf    | ref  | gender        | gender | 2       | const |    3 | Using where; Using index |
    +----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
    1 row in set (0.00 sec)
    
    mysql> EXPLAIN SELECT gender FROM mf WHERE gender='M';
    +----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
    | id | select_type | table | type | possible_keys | key    | key_len | ref   | rows | Extra                    |
    +----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
    |  1 | SIMPLE      | mf    | ref  | gender        | gender | 2       | const |   36 | Using where; Using index |
    +----+-------------+-------+------+---------------+--------+---------+-------+------+--------------------------+
    1 row in set (0.00 sec)
    
    mysql> EXPLAIN SELECT id FROM mf WHERE gender='F';
    +----+-------------+-------+------+---------------+--------+---------+-------+------+-------------+
    | id | select_type | table | type | possible_keys | key    | key_len | ref   | rows | Extra       |
    +----+-------------+-------+------+---------------+--------+---------+-------+------+-------------+
    |  1 | SIMPLE      | mf    | ref  | gender        | gender | 2       | const |    3 | Using where |
    +----+-------------+-------+------+---------------+--------+---------+-------+------+-------------+
    1 row in set (0.00 sec)
    
    mysql> EXPLAIN SELECT id FROM mf WHERE gender='M';
    +----+-------------+-------+------+---------------+------+---------+------+------+-------------+
    | id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
    +----+-------------+-------+------+---------------+------+---------+------+------+-------------+
    |  1 | SIMPLE      | mf    | ALL  | gender        | NULL | NULL    | NULL |   40 | Using where |
    +----+-------------+-------+------+---------------+------+---------+------+------+-------------+
    1 row in set (0.00 sec)
    
    mysql>
    

    Análise para InnoDB

    Quando os dados foram carregados como InnoDB, observe que todos os quatro EXPLAINplanos usaram o genderíndice. O terceiro e o quarto EXPLAINplanos usaram o genderíndice, embora os dados solicitados fossem id. Por quê? Porque idestá no PRIMARY KEYe todos os índices secundários têm ponteiros de referência de volta para o PRIMARY KEY(através do gen_clust_index ).

    Análise para MyISAM

    Quando os dados foram carregados como MyISAM, observe que os três primeiros EXPLAINplanos usaram o genderíndice. No quarto EXPLAINplano, o Query Optimizer decidiu não usar nenhum índice. Ele optou por uma varredura completa da tabela. Por quê?

    Independentemente do DBMS, os Otimizadores de Consulta operam com uma regra prática muito simples: se um índice estiver sendo rastreado como um candidato a ser usado para realizar a pesquisa e o Otimizador de Consulta calcular que deve pesquisar mais de 5% do número total de linhas da tabela:

    • uma varredura de índice completa é feita se todas as colunas necessárias para recuperação estiverem no índice selecionado
    • uma varredura completa da tabela caso contrário

    CONCLUSÃO

    Se você não tiver índices de cobertura adequados ou se a população-chave para qualquer tupla for superior a 5% da tabela, seis coisas devem acontecer:

    1. Chegue à conclusão de que você deve traçar o perfil das consultas
    2. Encontre todas as cláusulas WHERE, GROUP BYe ORDER BY` dessas consultas
    3. Formule índices nesta ordem
      • WHEREcolunas de cláusula com valores estáticos
      • GROUP BYcolunas
      • ORDER BYcolunas
    4. Evite Full Table Scans (consultas sem uma WHEREcláusula sensata)
    5. Evite Populações de Chave Inválidas (ou pelo menos armazene em cache essas Populações de Chave Inválidas)
    6. Decida sobre o melhor MySQL Storage Engine ( InnoDB ou MyISAM ) para as tabelas

    Eu escrevi sobre esta regra de ouro de 5% no passado:

    • May 07, 2012: MySQL EXPLAIN não mostra 'use index' para FULLTEXT
    • Mar 22, 2012: Por que o MySQL escolheu este plano de execução?
    • Mar 09, 2012: índice não sendo usado
    • Jan 18, 2012: A variável de status do MySQL Handler_read_rnd_next está crescendo muito
    • Dec 27, 2011: MySQL - maneira mais rápida de ALTER TABLE para InnoDB
    • Jul 29, 2011: MySQL Query Optimization : Indexação e Paginação
    • Jul 12, 2011: MySQL consulta muito lenta ao alterar um campo WHERE, apesar de nenhum índice/chave

    ATUALIZAÇÃO 14/11/2012 13:05 EDT

    Eu dei uma olhada na sua pergunta e no post original do SO . Então, pensei sobre o Analysis for InnoDBque mencionei antes. Coincide com a personmesa. Por quê?

    Tanto para tabelas mfcomo paraperson

    • Mecanismo de armazenamento é InnoDB
    • Chave Primária éid
    • O acesso à tabela é por índice secundário
    • Se a tabela fosse MyISAM, veríamos um EXPLAINplano completamente diferente

    Agora, observe a consulta da pergunta SO: select * from person order by age\G. Como não há WHEREcláusula, você exigiu explicitamente uma verificação completa da tabela . A ordem de classificação padrão da tabela seria por id(PRIMARY KEY) por causa de seu auto_increment e o gen_clust_index (também conhecido como Clustered Index) é ordenado por rowid interno . Ao ordenar pelo índice, lembre-se de que os índices secundários do InnoDB têm o rowid anexado a cada entrada de índice. Isso produz a necessidade interna de acesso total à linha a cada vez.

    A configuração ORDER BYem uma tabela InnoDB pode ser uma tarefa assustadora se você ignorar esses fatos sobre como os índices InnoDB são organizados.

    Voltando àquela consulta SO, já que você exigiu explicitamente uma verificação completa da tabela , IMHO, o MySQL Query Optimizer fez a coisa certa (ou pelo menos escolheu o caminho de menor resistência). Quando se trata de InnoDB e da consulta SO, é muito mais fácil executar uma varredura completa da tabela e mais algumas, filesortem vez de fazer uma varredura completa do índice e uma pesquisa de linha por meio do gen_clust_index para cada entrada de índice secundário.

    Não sou um defensor do uso de Index Hints porque ignora o plano EXPLAIN. Porém, se você realmente conhece seus dados melhor que o InnoDB, terá que recorrer a Index Hints, principalmente em consultas que não possuem WHEREcláusula.

    ATUALIZAÇÃO 14/11/2012 14:21 EDT

    De acordo com o livro Understanding MySQL Internals

    insira a descrição da imagem aqui

    O parágrafo 7º diz o seguinte:

    Os dados são armazenados em uma estrutura especial chamada índice clusterizado , que é uma árvore B com a chave primária atuando como o valor da chave e o registro real (em vez de um ponteiro) na parte de dados. Assim, cada tabela InnoDB deve ter uma chave primária. Se uma não for fornecida, uma coluna especial de ID de linha normalmente não visível para o usuário é adicionada para atuar como uma chave primária. Uma chave secundária armazenará o valor da chave primária que identifica o registro. O código da árvore B pode ser encontrado em innobase/btr/btr0btr.c .

    É por isso que afirmei anteriormente: é muito mais fácil executar uma verificação completa da tabela e, em seguida, algum tipo de arquivo, em vez de fazer uma verificação completa do índice e uma pesquisa de linha por meio do gen_clust_index para cada entrada de índice secundário . O InnoDB fará uma pesquisa de índice duplo toda vez . Isso soa meio brutal, mas são apenas os fatos. Novamente, leve em consideração a falta de WHEREcláusula. Isso, por si só, é a dica para o MySQL Query Optimizer fazer uma varredura completa da tabela.

    • 15
  2. Best Answer
    eggyal
    2013-05-28T23:50:53+08:002013-05-28T23:50:53+08:00

    Adaptado (com permissão) da resposta de Denis para outra pergunta no SO:

    Como todos os registros (ou quase todos) serão buscados pela consulta, geralmente é melhor você não ter nenhum índice. A razão para isso é que realmente custa algo para ler um índice.

    As you're going for the entire table, sequentially reading the table and sorting its rows in memory may be your cheapest plan. If you only need a few rows and most will match the where clause, going for the smallest index will do the trick.

    To understand why, picture the disk I/O involved.

    Suppose you want the whole table without an index. To do this, you read data_page1, data_page2, data_page3, etc., visiting the various disk pages involved in order, until you reach the end of the table. You then then sort and return.

    If you want the top 5 rows without an index, you'd sequentially read the entire table as before, while heap-sorting the top 5 rows. Admittedly, that's a lot of reading and sorting for a handful of rows.

    Suppose, now, that you want the whole table with an index. To do this, you read index_page1, index_page2, etc., sequentially. This then leads you to visit, say, data_page3, then data_page1, then data_page3 again, then data_page2, etc., in a completely random order (that by which the sorted rows appear in the data). The IO involved makes it cheaper to just read the whole mess sequentially and sort the grab bag in memory.

    If you merely want the top 5 rows of an indexed table, in contrast, using the index becomes the correct strategy. In the worst case scenario you load 5 data pages in memory and move on.

    Um bom planejador de consultas SQL, aliás, tomará sua decisão sobre usar ou não um índice com base na fragmentação de seus dados. Se buscar linhas em ordem significar zoom para frente e para trás na tabela, um bom planejador pode decidir que não vale a pena usar o índice. Por outro lado, se a tabela for agrupada usando o mesmo índice, é garantido que as linhas estarão em ordem, aumentando a probabilidade de serem usadas.

    Mas então, se você juntar a mesma consulta com outra tabela e essa outra tabela tiver uma cláusula where extremamente seletiva que pode usar um pequeno índice, o planejador pode decidir que é realmente melhor, por exemplo, buscar todos os IDs de linhas marcadas como foo, hash juntar as tabelas e classificá-las na memória.

    • 0

relate perguntas

  • Existem ferramentas de benchmarking do MySQL? [fechado]

  • Onde posso encontrar o log lento do mysql?

  • Como posso otimizar um mysqldump de um banco de dados grande?

  • Quando é o momento certo para usar o MariaDB em vez do MySQL e por quê?

  • Como um grupo pode rastrear alterações no esquema do banco de dados?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Como ver a lista de bancos de dados no Oracle?

    • 8 respostas
  • Marko Smith

    Quão grande deve ser o mysql innodb_buffer_pool_size?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    restaurar a tabela do arquivo .frm e .ibd?

    • 10 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

    Como selecionar a primeira linha de cada grupo?

    • 6 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
    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
    pedrosanta Listar os privilégios do banco de dados usando o psql 2011-08-04 11:01:21 +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
  • Martin Hope
    bernd_k Quando devo usar uma restrição exclusiva em vez de um índice exclusivo? 2011-01-05 02:32:27 +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