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.


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.


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;

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:


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


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


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.


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.

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.