web-dev-qa-db-fra.com

Suivi de la progression des requêtes MySQL

Pour commencer, je comprends qu’il n’ya pas d’appui direct pour quelque chose comme cela. Ce que je recherche, c’est un moyen quelconque de contourner le problème, ou une dérivation compliquée qui me permettrait d’obtenir un résultat à demi respectable.

Je travaille avec un cluster MySQL plutôt volumineux (tables> 400 millions de lignes) à l'aide du moteur de cluster.

Quelqu'un est-il au courant d'un moyen de récupérer directement ou par ailleurs dériver une indication assez précise (ou meilleure) de progrès à travers une longue requête dans mysql? J'ai des questions qui peuvent prendre jusqu'à 45 minutes et je dois déterminer si le traitement est à 10 ou 90%.

MODIFIER:

Comme demandé dans les commentaires, voici une version distillée et générée de one des requêtes qui mène à ma question initiale ...

SELECT `userId`
FROM    `openEndedResponses` AS `oe`
WHERE
    `oe`.`questionId` = 3 -- Zip code
    AND (REPLACE( REPLACE( `oe`.`value`, ' ', '' ), '-', '' ) IN ( '30071', '30106', '30122', '30134', '30135', '30168', '30180', '30185', '30187', '30317', '30004' ));

Cette requête est exécutée sur une table unique contenant environ 95 millions de lignes. Il faut 8 secondes pour exécuter la requête et 13 autres pour transférer les données (21 secondes au total). Compte tenu de la taille de la table et du fait que des fonctions de manipulation de chaînes sont utilisées, je dirais que cela tourne très vite. Cependant, pour l'utilisateur, il reste 21 secondes à apparaître bloqué ou inactif. Une indication de progrès serait idéale.

45
KOGI

Pour l'instant - pour ma situation très spécifique - il ne semble pas y avoir de vraie solution pour cela. Étant donné que je ne peux pas scinder ma requête en plusieurs requêtes plus petites et que cela s'avère contre-productif pour select count(*) d’abord, puis l’exécution de la requête "réelle" (double le temps d’exécution d’une requête déjà extrêmement lente), aucune des solutions de contournement ne semble viable. Peut-être que bientôt, MySQL supportera quelque chose comme ça 

2
KOGI

Je sais que la question est ancienne, mais je cherchais une réponse similaire lorsque je tentais de déterminer combien de temps encore ma mise à jour prendrait pour une requête de 250 m de lignes.

Si vous courez:

SHOW ENGINE INNODB STATUS \G

Ensuite, sous TRANSACTIONS, recherchez la transaction en question, examinez cette section:

---TRANSACTION 34282360, ACTIVE 71195 sec starting index read
mysql tables in use 2, locked 2
1985355 lock struct(s), heap size 203333840, 255691088 row lock(s), undo log entries 21355084

Le bit important est "Annuler les entrées du journal". Pour chaque ligne mise à jour, dans mon cas, il semblait ajouter une entrée de journal d'annulation (essayez de la réexécuter après quelques secondes et de voir combien ont été ajoutées).

Si vous passez à la fin du rapport d'état, vous verrez ceci: 

Number of rows inserted 606188224, updated 251615579, deleted 1667, read 54873415652
0.00 inserts/s, 1595.44 updates/s, 0.00 deletes/s, 3190.88 reads/s

Nous pouvons voir ici que la vitesse de mise à jour appliquée est de 1595,44 lignes par seconde (toutefois, si vous exécutez d'autres requêtes de mise à jour en tandem, cette vitesse peut être séparée entre vos requêtes).

Ainsi, je sais que 21 m ont été mis à jour et qu'il reste 229 m (250 m à 21 m) de rangs à parcourir.

229 000 000/1600 = 143,125 secondes (143,125/60)/60 = 39,76 heures

Il semblerait donc que je puisse me tourner les pouces pendant quelques jours. À moins que cette réponse ne soit fausse, dans ce cas, je la mettrai à jour quelque temps avant!

22
lightsurge

J'ai pu estimer quelque chose comme ceci en interrogeant le nombre de lignes à traiter puis en séparant le traitement en boucle, en ne travaillant que sur un sous-ensemble du total des lignes à la fois.

La boucle complète était plutôt compliquée, mais la logique de base était la suivante:

SELECT @minID = Min(keyColumn) FROM table WHERE condition
SELECT @maxID = Max(keyColumn) FROM table WHERE condition
SELECT @potentialRows = (@maxID - @minID) / @iterations

WHILE @minID < @maxID
BEGIN
    SET @breakID = @minID + @potentialRows
    SELECT columns FROM table WITH (NOLOCK, ...)
    WHERE condition AND keyColumn BETWEEN @minID AND @breakID

    SET @minID = @breakID + 1
END

Notez que cela fonctionne mieux si les ID sont distribués uniformément.

7
Dour High Arch

S'il s'agit d'une requête complexe que vous tentez, la commande EXPLAIN SQL ou l'Analyseur de requêtes MySQL peut vous aider à comprendre ce qui se passe. S'il s'agit simplement d'une requête volumineuse, vous pouvez essayer de créer une table temporaire avec SELECT INTO et/ou d'utiliser des clauses LIMIT/OFFSET dans les requêtes SELECT. Si vous utilisez LIMIT/OFFSET sur les tables d'origine, vous devrez peut-être définir le niveau de transaction sur sérialisable, IIRC, afin d'obtenir des lectures cohérentes lors de l'itération des données. Si vous créez d'abord une table temporaire, cette table doit rester cohérente malgré tout.

2
penguin359

Je ne pense pas que MySQL supporte Je suis sûr que MySQL ne prend en charge aucune indication sur la progression des requêtes en cours d'exécution. La seule solution consiste à optimiser/diviser les requêtes . Select pourrait être fractionné par id comme suggéré par Dour High Arch. Voici une requête de la table des 33 millions de lignes:

mysql> SELECT SQL_NO_CACHE min(id), max(id) FROM `urls`;
+---------+----------+
| min(id) | max(id)  |
+---------+----------+
|    5000 | 35469678 |
+---------+----------+
1 row in set (0.00 sec)

Vous feriez mieux d'utiliser un entier ou au moins un champ de date pour le fractionnement. Il devrait s'agir de primaire ou unique index et ne devrait pas autoriser les valeurs nulles. 

2
NickSoft

Il y a une réponse prometteuse à cette vieille question que j'ai trouvée ici , écrite par le baron Schwartz. Ce n'est pas une solution précise et complète, mais elle fournit des informations objectives pour les estimations, si vous n'exécutez cette requête que sur votre serveur.

Vous exécutez cette commande alors que la requête est déjà en cours d'exécution:

mysqladmin extended -r -i 10 | grep Handler
  • ce 10 est le nombre de secondes après lequel la commande se répète, attendez donc l'actualisation
  • ajoutez quelque chose comme -u root -p si vous avez besoin de vous authentifier
  • si vous connaissez exactement le gestionnaire que vous recherchez, vous pouvez mieux centrer le grep, par exemple Handler_read_rnd_next semble être bon pour les SELECT.
  • ignorer la première sortie, utiliser la seconde et les suivantes
  • utiliser Ctrl-C pour quitter

Maintenant, récupérez ce nombre et faites votre calcul. Déterminez les lignes traitées par seconde et, avec votre connaissance des tailles de table, vous pourrez peut-être obtenir une estimation assez précise du temps total.

Astuce supplémentaire gratuite: la commande ne semble pas entrer dans l'historique Bash (peut-être à cause de la sortie avec Ctrl-C, vous pouvez l'ajouter manuellement avec history -s mysqladmin extended -r -i 10 -u root -p | grep Handler

1
pgr

Voici ce que vous devez faire pour améliorer la requête suivante:

SELECT `userId`
FROM    `openEndedResponses` AS `oe`
WHERE
    `oe`.`questionId` = 3 -- Zip code
    AND (REPLACE( REPLACE( `oe`.`value`, ' ', '' ), '-', '' ) IN ( '30071', '30106', '30122', '30134', '30135', '30168', '30180', '30185', '30187', '30317', '30004' ));

Vous devrez vous assurer que oe.questionId est indexé. en supposant que 4 ou 5 peuvent être, disons, des noms de ville, pour lesquels vous souhaitez toujours autoriser des espaces.

En faisant cela, vous pourrez supprimer tous les REPLACE, ce qui permettra à MySQL d'utiliser un index dans oe.value.

MySQL va alors fusionner les deux index et vous donner le résultat beaucoup plus rapidement, en termes de traitement.

Dans le cas où vous avez plusieurs userId répétés; vous voudrez les grouper; de telle sorte que les entrées de l'index soient immédiatement supprimées. Vous devez toujours analyser l'intégralité de l'index fusionné. mais la taille de l'ensemble de résultats mettra moins de temps à être transférée; beaucoup moins que 13 secondes!

Essayez-le et tenez-nous au courant du résultat

Meilleur!

1
Nico Andrade

Si votre requête implique un balayage linéaire dans une grande table, vous pouvez souvent obtenir une excellente estimation en exécutant pmonitor sur le fichier contenant cette table. Incluez l'option --update, car MySQL ouvre les fichiers de table en mode de mise à jour.

Exemple:

 $ Sudo pmonitor --update --file =/home/mysql/ghtorrent/commits.MYD --interval = 5 

/Home/mysql/ghtorrent/commits.MYD 31,66% 
 /. home/mysql/ghtorrent/commits.MYD 33,16% ETA 0:03:42 
/home/mysql/ghtorrent/commits.MYD 34,85% ETA 0:03:24 
/home/mysql/ghtorrent/commits. MYD 36,43% ETA 0:03:32 
/Home/mysql/ghtorrent/commits.MYD 38,36% ETA 0:03:12 
/Home/mysql/ghtorrent/commits.MYD 40,21% ETA 0:03: 01
/home/mysql/ghtorrent/commits.MYD 41,95% ETA 0:02:54 
 [...] 
/Home/mysql/ghtorrent/commits.MYD 92,01% ETA 0:00: 24
/home/mysql/ghtorrent/commits.MYD 93.85% ETA 0:00:18 
/Home/mysql/ghtorrent/commits.MYD 95,76% ETA 0:00:12 
/Home/mysql /ghtorrent/commits.MYD 97.60% ETA 0:00:07 
/home/mysql/ghtorrent/commits.MYD 98.83% ETA 0:00:03 
/home/mysql/ghtorrent/commits.MYD 100% ETA 0:00:00 

Si vous ne connaissez pas le fichier à surveiller, exécutez pmonitor avec l'option --diff. Cela vous montrera le (s) fichier (s) où le progrès est fait.

Exemple

 $ Sudo pmonitor --update -diff --command = mysqld -i 60 
 [...] 
/Home/mysql/ghtorrent/projects.MYD 22,41% ETA 2:01:41 
/home/mysql/ghtorrent/projects.MYD 23.13% ETA 1:53:23 
/home/mysql/ghtorrent/projects.MYD 23,84% ETA 1:50:27 
0
Diomidis Spinellis

Que diriez-vous de partitionner votre table mysql pour pouvoir répartir la charge en lecture/écriture Essayez de limiter chaque partition à 50 millions de lignes (dépend évidemment de votre matériel)

0
Christian