Eu corro um EXPLAIN
:
mysql> explain select last_name from employees order by last_name;
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
| 1 | SIMPLE | employees | ALL | NULL | NULL | NULL | NULL | 10031 | Using filesort |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
1 row in set (0.00 sec)
Os índices da minha tabela:
mysql> show index from employees;
+-----------+------------+---------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------+------------+---------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| employees | 0 | PRIMARY | 1 | subsidiary_id | A | 6 | NULL | NULL | | BTREE | | |
| employees | 0 | PRIMARY | 2 | employee_id | A | 10031 | NULL | NULL | | BTREE | | |
| employees | 1 | idx_last_name | 1 | last_name | A | 10031 | 700 | NULL | | BTREE | | |
| employees | 1 | date_of_birth | 1 | date_of_birth | A | 10031 | NULL | NULL | YES | BTREE | | |
| employees | 1 | date_of_birth | 2 | subsidiary_id | A | 10031 | NULL | NULL | | BTREE | | |
+-----------+------------+---------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
5 rows in set (0.02 sec)
Existe um índice em last_name mas o otimizador não o utiliza.
Então eu faço:
mysql> explain select last_name from employees force index(idx_last_name) order by last_name;
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
| 1 | SIMPLE | employees | ALL | NULL | NULL | NULL | NULL | 10031 | Using filesort |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
1 row in set (0.00 sec)
Mas ainda assim o índice não é usado! O que eu estou fazendo errado aqui?
Tem a ver com o fato de o índice ser NON_UNIQUE
? BTW o last_name éVARCHAR(1000)
Atualização solicitada por @RolandoMySQLDBA
mysql> SELECT COUNT(DISTINCT last_name) DistinctCount FROM employees;
+---------------+
| DistinctCount |
+---------------+
| 10000 |
+---------------+
1 row in set (0.05 sec)
mysql> SELECT COUNT(1) FROM (SELECT COUNT(1) Count500,last_name FROM employees GROUP BY last_name HAVING COUNT(1) > 500) A;
+----------+
| COUNT(1) |
+----------+
| 0 |
+----------+
1 row in set (0.15 sec)
Na verdade, o problema aqui é que isso parece um índice de prefixo. Não vejo a definição da tabela na pergunta, mas
sub_part
= 700? Você não indexou a coluna inteira, portanto, o índice não pode ser usado para classificação e também não é útil como índice de cobertura. Ele só poderia ser usado para encontrar as linhas que "podem" corresponder aWHERE
e a camada do servidor (acima do mecanismo de armazenamento) teria que filtrar ainda mais as linhas correspondentes. Você realmente precisa de 1000 caracteres para um sobrenome?atualização para ilustrar: Eu tenho uma tabela de teste de tabela com pouco mais de 500 linhas, cada uma com o nome de domínio de um site em uma coluna
domain_name VARCHAR(254) NOT NULL
e sem índices.Com a coluna completa indexada, a consulta usa o índice:
Então, agora, vou descartar esse índice e indexar apenas os primeiros 200 caracteres de domain_name.
Voilá.
Observe também que o índice, com 200 caracteres, é maior que o valor mais longo na coluna...
... mas isso não faz qualquer diferença. Um índice declarado com um comprimento de prefixo só pode ser usado para pesquisas, não para classificação e não como índice de cobertura, pois não contém o valor completo da coluna, por definição.
Além disso, as consultas acima foram executadas em uma tabela InnoDB, mas executá-las em uma tabela MyISAM produz resultados praticamente idênticos. A única diferença neste caso é que a contagem do InnoDB para
rows
está ligeiramente desligada (541), enquanto MyISAM mostra o número exato de linhas (563), o que é um comportamento normal, já que os dois mecanismos de armazenamento lidam com mergulhos de índice de maneira muito diferente.Eu ainda afirmaria que a coluna last_name provavelmente é maior do que o necessário, mas ainda é possível indexar a coluna inteira, se você estiver usando o InnoDB e executando o MySQL 5.5 ou 5.6:
PROBLEMA #1
Veja a consulta
Não vejo uma cláusula WHERE significativa, nem o MySQL Query Optimizer. Não há incentivo para usar um índice.
PROBLEMA #2
Veja a consulta
Você deu um índice, mas o Query Opitmizer assumiu. Eu já vi esse comportamento antes ( Como forço um JOIN para usar um índice específico no MySQL? )
Por que isso deveria acontecer?
Sem uma
WHERE
cláusula, o Query Optimizer diz o seguinte para si mesmo:WHERE
cláusula?O Query Optimizer escolheu o caminho de menor resistência.
Você vai ficar um pouco chocado, mas aqui vai: Você sabia que o Query Optimizer irá lidar com o MyISAM de forma bem diferente?
Você provavelmente está dizendo HUH ???? COMO AS ????
MyISAM armazena os dados em um
.MYD
arquivo e todos os índices no.MYI
arquivo.A mesma consulta produzirá um plano EXPLAIN diferente porque o índice reside em um arquivo diferente dos dados. Por quê ? Aqui está o porquê:
last_name
coluna) já estão ordenados no.MYI
last_name
do índiceComo pode ter tanta certeza disso? Eu testei esta teoria de trabalho sobre como usar um armazenamento diferente gerará um plano EXPLAIN diferente (às vezes um melhor): Um índice deve cobrir todas as colunas selecionadas para que ele seja usado para ORDER BY?
Eu fiz uma resposta sobre porque um comentário não suportará formatação e o RolandoMySQL DBA falou sobre gen_clust_index e innodb. E isso é muito importante em uma tabela baseada em innodb. Isso vai além do conhecimento normal do DBA porque você precisa ser capaz de analisar o código C.
Você deve SEMPRE SEMPRE fazer uma CHAVE PRIMÁRIA ou uma CHAVE ÚNICA se estiver usando o Innodb. Se você não fizer isso, o innodb usará seu próprio ROW_ID gerado, o que pode fazer mais mal do que bem.
Vou tentar explicar fácil porque a prova é baseada em código C.
Primeiro problema
mutex_enter(&(dict_sys->mutex));
Esta linha garante que apenas um thread possa acessar dict_sys->mutex ao mesmo tempo. E se o valor já tiver sido mutexado ... sim, um thread tem que esperar para que você obtenha algo como um bom recurso aleatório como bloqueio de thread ou se você tiver mais tabelas sem sua própria PRIMARY KEY ou UNIQUE KEY, então você teria um bom recurso com innodb ' bloqueio de tabela ' não é esta não a razão pela qual o MyISAM foi substituído pelo InnoDB por causa do bom recurso que é chamado de bloqueio baseado em registro/linha.
Segundo problema
(0 == (id % DICT_HDR_ROW_ID_WRITE_MARGIN))
os cálculos do módulo (%) são lentos não são bons se você estiver inserindo em lote porque precisa ser recalculado toda vez ... e porque DICT_HDR_ROW_ID_WRITE_MARGIN (valor 256) é uma potência de dois, isso pode ser feito muito mais rápido ..
(0 == (id & (DICT_HDR_ROW_ID_WRITE_MARGIN - 1)))
Nota lateral se o compilador C foi configurado para otimizar e é um bom otimizador, o otimizador C corrigirá o código "pesado" para a versão mais leve
lema da história sempre crie sua própria PRIMARY KEY ou certifique-se de ter um índice UNIQUE ao criar uma tabela desde o início