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é.
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.
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
À 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.
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.
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.
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 SELECT
s 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.
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;'
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
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.