A consulta de seleção leva mais tempo do que deveria

9

Eu tenho uma tabela de banco de dados MySQL com quase 23 milhões de registros. Esta tabela não possui chave primária, porque nada é exclusivo. Possui 2 colunas, ambas indexadas. Abaixo está sua estrutura:

insira a descrição da imagem aqui

Abaixo estão alguns de seus dados:

insira a descrição da imagem aqui

Agora, eu executei uma consulta simples:

SELECT `indexVal` FROM `key_word` WHERE `hashed_word`='001'

Infelizmente, isso levou mais de 5 segundos para recuperar os dados e mostrá-los para mim. Minha tabela futura terá 150 bilhões de registros, então esse tempo é muito, muito alto.

Eu executei o Explaincomando para ver o que está acontecendo. O resultado está abaixo.

insira a descrição da imagem aqui

Então eu executei o perfil usando o comando abaixo.

SET profiling=1;
SELECT `indexVal` FROM `key_word` WHERE `hashed_word` = '001';
SHOW profile;

Abaixo está o resultado da criação de perfil:

insira a descrição da imagem aqui

Abaixo estão mais algumas informações sobre minha tabela:

insira a descrição da imagem aqui

Então, por que isso está demorando tanto? Eles também são indexados! No futuro, eu tenho que executar muitos LIKEcomandos, então isso está levando muito tempo. O que deu errado?

Suco de limão
fonte
"Esta tabela não possui chave primária, porque nada é exclusivo." Sim, certo ... Hora de reexaminar seu projeto. Todas as tabelas devem ter uma chave primária (ou única).
precisa saber é o seguinte

Respostas:

10

Você perguntou " por que isso está demorando muito ?". Você também disse " Infelizmente, isso levou mais de 5 segundos para recuperar os dados e mostrá-los para mim ". Além disso, você relatou a saída de criação de perfil da sua consulta.

Como você pode ver, a soma de vezes relatada pelo criador de perfil para cada etapa conta para 0,000154 segundos. Portanto, do ponto de vista do criador de perfil, a consulta foi concluída em um determinado momento (0,000154).

Então, por que você está obtendo resultados em " ... mais de 5 segundos? ".

Você disse que está filtrando uma tabela de 23 milhões de registros com um campo de 3 caracteres. Infelizmente você não nos diz quantos registros sua consulta está retornando ... mas, graças ao EXPLAIN SELECT fornecido, parece que sua consulta retornou 336052 registros.

Parece também que toda a sua atividade é executada através de alguma GUI (PHPMyAdmin?).

Portanto, depois de tudo isso, podemos reformular sua pergunta original como:

"por que estou recebendo, dentro da minha GUI, 336.052 registros mostrados em mais de 5 segundos, se o tempo de execução do MySQL para a consulta relacionada é de 0,000154 segundos?"

A resposta, na minha opinião, é bastante simples: 5 segundos é o tempo (realmente baixo) para permitir que 336.052 registros viajem pelo caminho: mecanismo MySQL => bibliotecas cliente MySQL => módulo MySQL PHP => módulo MySQL PHP => Apache => rede = > sua pilha TCP / IP do PC => Navegador => analisador / construtor DOM / etc. => Página HTML renderizada.

Quanto à minha experiência anterior, o tempo exigido pela transmissão de resultados é "normalmente" muito maior do que o tempo necessário para recuperar esses dados. Isso é especialmente verdadeiro quando bibliotecas como PHP-MySQL ou Perl-DBD-MySQL estão envolvidas: elas realmente requerem muito tempo para recuperar os registros, depois que o MySQL identificou corretamente (... e extraiu) todos eles.

Como resolver este problema?

Novamente, com muita facilidade: você tem certeza de que precisa de TODO o registro 336.052, em um único conjunto de dados inteiro?

  • Se sua resposta for realmente "SIM! Preciso de todos eles", seu aplicativo manipulará PAGINATION e / ou USER-Interaction por si só e ... depois de reunir todos esses dados, provavelmente gastará muito tempo interagir com o usuário sem exigir nenhuma interação adicional do MySQL. Nesse caso, aguardar 5 segundos (ou mais) não deve ser um problema;

  • Se sua resposta for "NÃO, eu quero lidar com um tamanho de conjunto de dados mais 'humano'", você precisará refinar sua consulta (pelo menos) para que ele devolva a você um conjunto de dados mais "humano" (dezenas ou, centenas, no máximo, registros). Nesse caso, aposto que você obterá o resultado em menos tempo.


BTW: esse é exatamente o mesmo problema que você teve neste outro post , na ServerFault: 88 segundos para permitir que os registros de 132M viajem pelo caminho mágico .... not-mysql-strictly related :-)

Damiano Verzulli
fonte
Estou esperando uma resposta do op.
Jnanaranjan 27/01
5
  1. Verifique o mysql innodb_buffer_pool_size . Deve ser grande o suficiente - quanto mais, melhor. Mas não muito para evitar a troca de SO.

    show variables like 'innodb_buffer_pool_size'

    mostrará o tamanho do buffer em bytes.

  2. Verifique a consulta mais de uma vez. A primeira execução pode ser muito longa, pois os dados devem ser lidos do disco na memória. Quando você está executando uma consulta pela primeira vez, os dados ainda não estão no buffer do innodb e precisam ser lidos no disco. O que é muito mais lento do que se os dados já estivessem no cache. Portanto, execute a consulta algumas vezes para garantir que ela seja veiculada no cache.

  3. Desative o cache da consulta, pois cada execução subsequente será realizada a partir dele e influenciará os resultados do teste. Existe um mecanismo no MySQL, chamado "cache de consulta", que é projetado para armazenar consultas junto com seus resultados. Portanto, na segunda vez que o MySQL é solicitado a executar a consulta, ele pode ignorar a execução e recuperar os resultados do cache da consulta.

  4. Considere usar um "índice de cobertura":

    ALTER TABLE key_word ADD KEY IX_hashed_word_indexVal (hashed_word, indexVal);

Isso seria muito mais eficiente, pois o MySQL pode atender a solicitação de consulta apenas do índice.

Boris
fonte