web-dev-qa-db-fra.com

La requête de sélection prend plus de temps qu'elle ne devrait

J'ai une table de base de données MySQL avec près de 23 millions d'enregistrements. Cette table n'a pas de clé primaire, car rien n'est unique. Il a 2 colonnes, les deux sont indexées. Voici sa structure:

enter image description here

Voici certaines de ses données:

enter image description here

Maintenant, j'ai exécuté une simple requête:

SELECT `indexVal` FROM `key_Word` WHERE `hashed_Word`='001'

Malheureusement, cela a pris plus de 5 secondes pour récupérer les données et me les montrer. Mon futur tableau comptera 150 milliards d'enregistrements, donc cette fois est très très élevé.

J'ai exécuté la commande Explain pour voir ce qui se passe. Le résultat est ci-dessous.

enter image description here

Ensuite, j'ai exécuté le profil à l'aide de la commande ci-dessous.

SET profiling=1;
SELECT `indexVal` FROM `key_Word` WHERE `hashed_Word` = '001';
SHOW profile;

Voici le résultat du profilage:

enter image description here

Voici quelques informations supplémentaires sur ma table:

enter image description here

Alors, pourquoi cela prend-il si longtemps? Ils sont aussi indexés! À l'avenir, je dois exécuter beaucoup de commandes LIKE, donc cela prend trop de temps. Qu'est-ce qui a mal tourné?

9
Lemon Juice

Vous avez demandé " pourquoi cela prend trop de temps ?". Vous avez également dit " Malheureusement, cela a pris plus de 5 secondes pour récupérer les données et me les montrer ". Vous avez également signalé la sortie de profilage de votre requête.

Comme vous pouvez le constater vous-même, la somme des temps rapportés par le profileur pour chaque étape compte pour 0,000154 secondes. Ainsi, du point de vue du profileur, la requête a été terminée dans un tel délai (0.000154).

Alors pourquoi obtenez-vous des résultats en " ... plus de 5 secondes? ".

Vous avez dit que vous filtriez une table d'enregistrement de 23 millions avec un champ de 3 caractères. Malheureusement, vous ne nous dites pas combien d'enregistrements votre requête renvoie ... mais grâce au EXPLAIN SELECT fourni, il semble que votre requête ait renvoyé 336052 enregistrements.

Il semble également que toute votre activité passe par une interface graphique (PHPMyAdmin?).

Donc, après tout ce qui précède, nous pouvons reformuler votre question initiale comme suit:

"Pourquoi est-ce que j'obtiens, dans mon interface graphique, 336.052 enregistrements affichés en plus de 5 secondes, si le temps d'exécution MySQL pour la requête associée est de 0.000154 secondes?"

La réponse, à mon avis, est assez simple: 5 secondes est le temps (vraiment bas, en effet) pour laisser 336.052 enregistrements voyager le long du chemin: moteur MySQL => bibliothèques client MySQL => PHP Module MySQL => Apache => Network => votre pile TCP/IP PC => Browser => DOM parser/builder/etc. => Page HTML rendue.

Quant à mon expérience précédente, le temps requis par la transmission des résultats est "normalement" beaucoup plus élevé que le temps nécessaire pour récupérer ces données. Cela est particulièrement vrai lorsque des bibliothèques comme PHP-MySQL ou Perl-DBD-MySQL sont impliquées: elles nécessitent vraiment beaucoup de temps pour récupérer les enregistrements, après MySQL les a correctement identifiés (... et extraits).

Comment résoudre ce problème?

Encore une fois, assez facilement: êtes-vous vraiment sûr d'avoir besoin [~ # ~] de tous les [~ # ~] de l'enregistrement 336.052, en un seul, ensemble, ensemble de données?

  • Si votre réponse est vraiment "OUI! J'ai besoin de tous", votre application gérera PAGINATION et/ou USER-Interaction par elle-même et ... une fois qu'elle aura rassemblé toutes ces données, elle passera probablement beaucoup de temps. interagir avec l'utilisateur sans nécessiter d'autre interaction MySQL. Dans un tel cas, attendre 5 secondes (voire plus) ne devrait pas être un problème;

  • Si votre réponse est "NON, je veux gérer une taille de jeu de données plus" humaine "", vous devez affiner votre requête (au moins) afin qu'elle vous redonne un jeu de données plus "humain" (des dizaines ou, des centaines, tout au plus, des enregistrements). Dans un tel cas, je parie que vous obtiendrez votre résultat dans un temps plus court.


BTW: c'est exactement le même problème que vous avez rencontré dans cet autre article , à ServerFault: 88 secondes pour laisser 132M d'enregistrements voyager le long du chemin magique .... pas-mysql-strictement lié :-)

10
Damiano Verzulli
  1. Vérifiez le mysql innodb_buffer_pool_size. Il devrait être assez grand - plus il y en a, mieux c'est. Mais pas trop pour éviter l'échange de système d'exploitation.

    show variables like 'innodb_buffer_pool_size'
    

    affichera la taille du tampon en octets.

  2. Vérifiez la requête plusieurs fois. La première exécution peut être trop longue car les données doivent être lues du disque dans la mémoire. Lorsque vous exécutez la requête pour la première fois, les données ne sont toujours pas dans le tampon innodb et doivent être lues sur le disque. Ce qui est beaucoup plus lent que si les données étaient déjà dans le cache. Exécutez donc la requête plusieurs fois pour vous assurer qu'elle est servie à partir du cache.

  3. Désactivez le cache de requêtes car chaque exécution conséquente en sera réalisée et biaisera les résultats du test. Il existe un mécanisme dans MySQL, appelé "cache de requêtes", qui est conçu pour stocker les requêtes avec leurs résultats. Ainsi, la deuxième fois que MySQL est invité à exécuter la requête, il peut contourner l'exécution et récupérer les résultats du cache de requêtes.

  4. Pensez à utiliser un "indice de couverture":

    ALTER TABLE key_Word ADD KEY IX_hashed_Word_indexVal (hashed_Word, indexVal);
    

Ce serait beaucoup plus efficace, car MySQL peut alors répondre à la demande de requête à partir de l'index seul.

5
Boris