Indexação do MySQL VarChar

10

Estou tentando indexar meu blogentriesbanco de dados para obter um melhor desempenho, mas encontrei um problema.

Aqui está a estrutura:

CREATE TABLE IF NOT EXISTS `blogentries` (
  `id_id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `title_id` varchar(100) COLLATE latin1_german2_ci NOT NULL,
  `entry_id` varchar(5000) COLLATE latin1_german2_ci NOT NULL,
  `date_id` int(11) NOT NULL,
  PRIMARY KEY (`id_id`)
)
ENGINE=MyISAM
DEFAULT CHARSET=latin1
COLLATE=latin1_german2_ci
AUTO_INCREMENT=271;

Uma consulta como a seguinte usa o índice corretamente:

EXPLAIN SELECT id_id,title_id FROM blogentries ORDER by id_id DESC
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +
| id | select_type | mesa | tipo | possible_keys | chave key_len | ref linhas | Extra |
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +
| 1 | SIMPLES | blogentries | índice | NULL PRIMÁRIO 114 NULL 126 Usando índice |
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +

No entanto, quando adiciono o entry_idna SELECTconsulta, ele usa o filesort

EXPLAIN SELECT id_id,title_id,entry_id FROM blogentries ORDER by id_id DESC
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +
| id | select_type | mesa | tipo | possible_keys | chave key_len | ref linhas | Extra |
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +
| 1 | SIMPLES | blogentries | TUDO | NULL NULL NULL NULL 126 Usando filesort |
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +

Fiquei me perguntando por que isso está acontecendo e como posso evitá-lo? É devido ao VarChar, e isso deve ser alterado para outra coisa?

Estou tentando fazer com que todas as minhas consultas usem o índice, pois estou com valores Handler_read_rnde Handler_read_rnd_nextaltos.

Se você precisar de outras informações, também posso publicá-las.


fonte
filesort significa que está executando a classificação no disco.
1212 Kermit
Tente adicionar WHERE 1=1à sua segunda consulta.
1212 Kermit
Qual versão do MySQL é essa? Qual é o tamanho do seu buffer de classificação ( SELECT @@sort_buffer_size)?
@njk filesort é o resultado da parte 'ORDER BY' da consulta
11
@TashPemhiwa Não necessariamente, veja a primeira declaração.
27612 Kermit

Respostas:

6

Como você não possui uma WHEREcláusula em nenhuma consulta, retornará todas as linhas nos dois casos, portanto, acho que o uso ou não uso do índice teria muito pouco impacto no desempenho nesses exemplos.

Joe Stefanelli
fonte
Certamente o MySQL deve usar o índice para o ORDER BY?
eggyal 12/10/12
@eggyal Não se for muito grande para memória.
1212 Kermit
@njk: Isso não faz sentido ... ele pode percorrer o índice, em ordem, sem precisar carregar tudo na memória. Os resultados seriam classificados sem a necessidade de executar a classificação do arquivo.
eggyal 12/10/12
@eggyal Gostaria de questionar o tamanho de varchar(5000).
Kermit
@njk: Mas essa coluna não está no índice nem está sendo usada no tipo.
eggyal 12/10/12
2

Conforme documentado em ORDER BYOtimização :

Para consultas lentas para as quais filesortnão é usado, tente diminuir max_length_for_sort_datapara um valor apropriado para acionar a filesort.

Em seu artigo no blog O que exatamente é read_rnd_buffer_size , Peter Zaitsev explica:

Para mim, isso significa que, desde o MySQL 4.1, essa opção é usada em casos estreitos - se você recuperar poucos campos (menos que max_length_for_sort_data ), os dados deverão ser armazenados no buffer de classificação e no arquivo de classificação, para que não haja necessidade de read_rnd_buffer, se as colunas selecionadas são longos e, portanto, maiores que max_length_for_sort_data, isso frequentemente significa que há algumas colunas TEXT / BLOB entre elas. No entanto, seria usado se houver um grande número de colunas ou se forem usadas colunas VARCHAR longas - são necessárias apenas duas UTF8 VARCHAR (255) para criar uma linha maior que max_length_for_sort_data em sua apresentação estática.

Isso sugere que max_length_for_sort_datahá um limite no tamanho total das colunas que você está selecionando, acima do qual a filesortserá usado em vez de uma classificação baseada em índice.

No seu caso, selecionar entry_id(5002 bytes) assume o tamanho total sobre o valor padrão de 1KiB dessa variável e, portanto, filesorté usado. Para aumentar o limite para 8KiB, você pode:

SET SESSION max_length_for_sort_data = 8192;
eggyal
fonte
Eu tenho uma tabela com uma configuração muito semelhante a esta e essa configuração não parece desencadear nenhuma alteração no uso do filesort.
@muffinista: Isso é interessante. Suponho que possa estar relacionado a algumas das outras configurações de buffer, de acordo com a resposta de @ RolandoMySQLDBA ?
eggyal 25/10/12
2

Você obteve muitas respostas interessantes aqui, mas ninguém respondeu exatamente à pergunta - por que isso está acontecendo? Pelo que entendi, quando uma consulta SELECT contém dados de comprimento variável no MySQL, e não há índice que corresponda a TODAS as colunas solicitadas, ele sempre usará um tipo de arquivo. O tamanho dos dados não é muito relevante aqui. É difícil encontrar uma resposta direta a essa pergunta na documentação do MySQL, mas aqui está um bom post em que alguém está enfrentando um problema muito semelhante ao seu.

Veja também: 10 dicas para otimizar consultas MySQL (que não são ruins) .

Portanto, se for viável ter um índice em entry_id, você poderá adicioná-lo e estar pronto. Mas duvido que seja uma opção, então o que fazer?

Se você deve fazer algo sobre isso é uma questão separada. É importante saber que 'filesort' é mal nomeado no MySQL - é realmente apenas o nome do algoritmo usado para classificar essa consulta específica e, em muitos casos, a classificação realmente acontece na memória. Se você não espera que esta tabela cresça muito, provavelmente não é grande coisa.

Por outro lado, se esta tabela tiver um milhão de linhas, você poderá ter um problema. Se você precisar oferecer suporte à paginação de consultas nesta tabela, poderá ter um problema de desempenho realmente sério aqui. Nesse caso, particionar seus dados de tamanho variável em uma nova tabela e executar um JOIN para recuperá-los é uma otimização válida a ser considerada.

Aqui estão algumas outras respostas sobre SO que abordam essa questão:

Comunidade
fonte
A primeira consulta do OP " contém dados de comprimento variável no MySQL, e não há índice que corresponda a TODAS as colunas solicitadas ", mas filesortaparentemente não foi usado nesse caso. Eu também acho que mesmo classificar uma tabela pequena na memória pode ser um impacto inaceitável no desempenho: por exemplo, se a consulta for realizada muito (e a tabela for alterada para que os caches não possam ser usados).
eggyal 25/10/12
Não tenho tempo para testá-lo, mas estou imaginando se isso é acionado com um VARCHAR que requer 2 bytes para armazenar o comprimento, conforme especificado em dev.mysql.com/doc/refman/5.1/en/char. html - para que a primeira consulta caiba dentro desse limite, mas a segunda não.
0

Tente adicionar uma WHEREcláusula em suas consultas.

O índice pode ser usado mesmo que o ORDER BY não corresponda exatamente ao índice, desde que todas as partes não utilizadas do índice e todas as colunas ORDER BY extras sejam constantes na cláusula WHERE . Em alguns casos, o MySQL não pode usar índices para resolver a ORDER BY , embora ainda use índices para encontrar as linhas que correspondem à cláusula WHERE .

http://dev.mysql.com/doc/refman/5.0/en/order-by-optimization.html


fonte
Mas, neste caso, ORDER BY ele corresponde exatamente ao índice, portanto não há necessidade de ter uma WHEREcláusula.
eggyal
Eu tenho uma cláusula "where" na consulta real no site, então sei que essa não é a causa da classificação do arquivo. Gostaria de saber se é o uso de varchar?
0

Na medida do meu conhecimento, varchar pode conter apenas um máximo de 8000 bytes, que são aproximadamente 4000 caracteres. Assim, 5000 parece exceder o limite de armazenamento e, nesse caso, provavelmente a razão pela qual a classificação está ficando confusa.

"varchar [(n | max)] Dados de caracteres não Unicode de comprimento variável. n pode ser um valor de 1 a 8.000. max indica que o tamanho máximo de armazenamento é 2 ^ 31-1 bytes. O tamanho de armazenamento é o valor real comprimento dos dados inseridos + 2 bytes. Os dados inseridos podem ter 0 caracteres. Os sinônimos do SQL-2003 para varchar variam de char ou de caracteres. "

Espero que isso responda sua pergunta


fonte
Conforme documentado em The CHARe VARCHARTypes : "Os valores nas colunas VARCHAR são cadeias de comprimento variável. O comprimento pode ser especificado como um valor de 0 a 255 antes do MySQL 5.0.3 e de 0 a 65.535 na versão 5.0.3 e posterior. o comprimento máximo de a VARCHARno MySQL 5.0.3 e posterior está sujeito ao tamanho máximo de linha (65.535 bytes, que é compartilhado entre todas as colunas) e ao conjunto de caracteres usado. "
eggyal
0

Você só tem 126 linhas na sua tabela. Mesmo que todas as linhas tenham o tamanho máximo de cerca de 5 KB, isso significa que o tamanho total para leitura do disco é de apenas 600 KB - isso não é muito. Para ser franco, é uma quantidade muito pequena, provavelmente menor que o tamanho do cache da maioria das unidades de disco modernas.

Agora, se o servidor precisar recuperar seus dados para atender sua consulta, a operação mais cara será lê-los do disco. Porém, ler de acordo com a ordem do índice nem sempre é a maneira mais rápida de fazê-lo, especialmente quando a quantidade de dados é tão pequena.

No seu caso, é MUITO mais eficiente ler dados inteiros da tabela do disco como um único bloco na memória (provavelmente em apenas uma operação de leitura ou busca de disco) e depois classificá-los na RAM para satisfazer ORDER BY, que é instantâneo em comparação ao disco leia operação. Se o servidor ler seus dados de acordo com o índice, ele precisará emitir até 126 (oops!) Operações de leitura, buscando e retornando no mesmo arquivo de dados várias vezes.

Em outras palavras, a varredura seqüencial nem sempre é uma coisa ruim, e o mysql não é necessariamente estúpido. Se você tentar forçar o mysql a usar esse índice, provavelmente funcionará mais lentamente que a verificação seqüencial que você possui atualmente.

E a razão pela qual estava usando o índice quando o campo de 5 KB não foi incluído é porque os dados recuperados não constituíam 99% dos dados na tabela. Quando você incluiu seu campo de 5 KB, agora a consulta precisa ler 99% dos dados e é mais barato ler tudo e classificá-lo na memória posteriormente.

mvp
fonte
Parece que você está confundindo várias coisas em Como evitar verificações completas da tabela , relacionadas ao uso do índice em JOINcondições e WHEREcláusulas satisfatórias , não ORDER BYcláusulas.
eggyal 24/10/12
Exatamente o oposto. Nesse caso em particular, a varredura completa da tabela é BOA, simplesmente porque é MAIS RÁPIDA do que a leitura por ordem do índice.
0

Qual versão do MySQL você está usando?

No 5.1, tentei configurar o seu cenário e preenchi alguns dados fictícios. Usando os SQLs que você forneceu, só recebo uma varredura de tabela a cada vez, de acordo com o EXPLAIN. Por padrão, quando você usa a ordem do MYSQL, recorre à classificação do arquivo, mesmo que o índice primário seja usado na ordem de.


fonte