web-dev-qa-db-fra.com

Comment puis-je arrêter une requête MySQL si elle prend trop de temps?

Est-il possible de temporiser une requête dans MySQL?

Autrement dit, si une requête dépasse le temps que je spécifie, elle sera tuée par MySQL et elle renverra une erreur au lieu d'attendre l'éternité.

44
acheruns

Pour cela, il existe un script Nice Perl sur CPAN: http://search.cpan.org/~rsoliv/mysql-genocide-0.03/mysql-genocide

Il suffit de le planifier pour qu'il s'exécute avec les paramètres appropriés. Créez un fichier CRONtab /etc/cron.d/mysql_query_timeout pour planifier son exécution à chaque minute:

* * * * * root /path/to/mysql-genocide -t 7200 -s -K

Où 7200 est le temps d'exécution maximal autorisé en secondes. Le commutateur -s filtre tout sauf les requêtes SELECT. Le commutateur -K demande au script de supprimer les processus correspondants.

L'utilisateur root doit pouvoir exécuter les outils mysql locaux sans authentification, sinon vous devrez fournir des informations d'identification sur la ligne de commande.

10
Erik

Je viens de configurer le script bash suivant en tant que tâche cron pour accomplir cela avec MySQL 5.0 (tue toute requête qui s'exécute depuis plus de 30 secondes). Le partager ici au cas où cela s'avérerait utile à quiconque (excuses si mon style de script bash est inefficace ou atroce, ce n'est pas mon langage de développement principal):

#!/bin/bash
linecount=0
processes=$(echo "show processlist" | mysql -uroot -ppassword)
oldIfs=$IFS
IFS='
'
echo "Checking for slow MySQL queries..."
for line in $processes
do
    if [ "$linecount" -gt 0 ]
        then
            pid=$(echo "$line" | cut -f1)
            length=$(echo "$line" | cut -f6)
            query=$(echo "$line" | cut -f8)
            #Id User    Host    db  Command Time    State   Info
            if [ "$length" -gt 30 ]
                then
                    #echo "$pid = $length"
                    echo "WARNING:  Killing query with pid=$pid with total execution time of $length seconds! (query=$query)"
                    killoutput=$(echo "kill query $pid" | mysql -uroot -ppassword)
                    echo "Result of killing $pid:  $killoutput"
            fi
    fi
    linecount=`expr $linecount + 1`
done
IFS=$oldIfs
9
aroth

À partir de MySQL 5.1, vous pouvez créer une procédure stockée pour interroger la table information_schmea.PROCESSLIST pour toutes les requêtes qui correspondent à vos critères de "longue durée", puis itérer sur un curseur pour les tuer. Configurez ensuite cette procédure pour qu'elle s'exécute de manière récurrente dans le planificateur d'événements.

Voir: http://forge.mysql.com/tools/tool.php?id=106

8

Je pensais que ça faisait un peu plus longtemps, mais selon this ,

MySQL 5.7.4 introduit la possibilité de définir des délais d'exécution côté serveur, spécifiés en millisecondes, pour les instructions SELECT en lecture seule de niveau supérieur.

SELECT 
MAX_STATEMENT_TIME = 1000 --in milliseconds
* 
FROM table;

Notez que cela ne fonctionne que pour les instructions SELECT en lecture seule.

8
Westy92

Le forum MySQL a quelques discussions à ce sujet.

Cet article détaille comment configurer les délais d'attente sur le serveur en utilisant innodb_lock_wait_timeout.

Voici un moyen de le faire par programme , en supposant que vous utilisez JDBC.

7
David

Je pense que cette vieille question a besoin d'une réponse mise à jour.

Vous pouvez définir un délai d'expiration GLOBAL pour toutes vos requêtes SELECT en lecture seule comme ceci:

SET GLOBAL MAX_EXECUTION_TIME=1000;

Le temps spécifié est en millisecondes.

Si vous ne souhaitez que le délai d'expiration pour une requête spécifique, vous pouvez le définir en ligne comme ceci:

SELECT /*+ MAX_EXECUTION_TIME(1000) */ my_column FROM my_table WHERE ...

MySQL renvoie une erreur au lieu d'attendre l'éternité.

Notez que cette méthode ne fonctionne que pour les SELECTs en lecture seule. Si une instruction SELECT est déterminée comme n'étant pas en lecture seule, tout temporisateur défini pour elle est annulé et le message REMARQUE suivant est signalé à l'utilisateur:

Note 1908 Select is not a read only statement, disabling timer

Pour les instructions avec sous-requêtes, il limite uniquement les SELECT premiers. Il ne s'applique pas aux instructions SELECT dans les programmes stockés. L'utilisation de l'indicateur MAX_EXECUTION_TIME Dans les instructions SELECT dans un programme stocké sera ignorée.

3
Ali Hashemi

Je ne pense pas que l'egrep ci-dessus trouverait "2000".
Pourquoi ne pas simplement essayer de sélectionner l'ID également, et d'éviter tout ce truc chic de Shell:

mysql -e 'select id from information_schema.processlist where info is not null and time > 30;'
3
fred

Voici mon script:

mysql -e 'show processlist\G' |\
egrep -b5 'Time: [6-9]{3,10}' |\
grep 'Id:' |\
cut -d':' -f2 |\
grep -v '155' |\ ## Binary Log PID
sed 's/^ //' |\
while read id
do
    mysql -e "kill $id;"
done
2
Srinivas Mutyala

Depuis MySQL 5.7.8, il existe une option max_execution_time qui définit le délai d'exécution pour les instructions SELECT.

2
Alexander Ushakov