Comment tuer automatiquement les requêtes MySQL lentes après N secondes?

16

Je recherche un script bash bien testé (ou une solution alternative) pour le faire, afin d'éviter que max_connection ne soit épuisé. Je sais qu'il combat les symptômes, mais j'ai vraiment besoin d'un tel script comme solution à court terme.

alfish
la source
Quelle version de MySQL utilisez-vous ???
RolandoMySQLDBA
la version mysql est 5.5
alfish

Réponses:

21

consultez la commande pt-kill de la boîte à outils percona .

et .. commencez à surveiller votre système - munin , cacti avec de meilleurs modèles de cactus pour mysql , n'importe quoi pour que vous ayez une idée de ce qui se passe. la journalisation des requêtes lentes mysql sera également une bonne idée.

pQd
la source
Merci pour les suggestions, mais cherchez vraiment un script "unique".
alfish
5
pt-kill est très bien testé et résout votre problème exact. Il faut environ 10 minutes pour comprendre les paramètres de ligne de commande et le faire fonctionner. Que veux-tu de plus?
Aaron Brown
12

Si vous avez MySQL 5.1 où la liste de processus se trouve dans INFORMATION_SCHEMA, vous pouvez le faire pour générer les commandes KILL QUERY en bloc à partir du client mysql pour une requête de plus de 20 minutes (1200 secondes):

SELECT GROUP_CONCAT(CONCAT('KILL QUERY ',id,';') SEPARATOR ' ') KillQuery
FROM information_schema.processlist WHERE user<>'system user'
AND time >= 1200\G

Vous pouvez faire des clauses WHERE sur le champ INFO pour rechercher une requête spécifique, le champ TIME sur les requêtes de longue durée ou le champ DB sur une base de données spécifique.

Si vous êtes root @ localhost, vous devriez avoir tous les privilèges pour l'exécuter comme suit

SECONDS_TOO_LONG=1200
KILLPROC_SQLSTMT="SELECT GROUP_CONCAT(CONCAT('KILL QUERY ',id,';') SEPARATOR ' ') KillQuery FROM information_schema.processlist WHERE user<>'system user' AND time >= ${SECONDS_TOO_LONG}"
mysql -uroot -ppassword -ANe"${KILLPROC_SQLSTMT}" | mysql -uroot -ppassword

Vous pouvez créer ceci comme suit:

SECONDS_TOO_LONG=1200
QUERIES_RUNNING_TOO_LONG=`mysql -uroot -ppassword -ANe"SELECT COUNT(1) FROM information_schema.processlist WHERE user<>'system user' AND time >= ${SECONDS_TOO_LONG}"`
if [ ${QUERIES_RUNNING_TOO_LONG} -gt 0 ]
then
    KILLPROC_SQLSTMT="SELECT GROUP_CONCAT(CONCAT('KILL QUERY ',id,';') SEPARATOR ' ') KillQuery FROM information_schema.processlist WHERE user<>'system user' AND time >= ${SECONDS_TOO_LONG}"
    mysql -uroot -ppassword -ANe"${KILLPROC_SQLSTMT}" | mysql -uroot -ppassword
fi

Voici une autre variante:

SECONDS_TOO_LONG=1200
QUERIES_RUNNING_TOO_LONG=`mysql -uroot -ppassword -ANe"SELECT COUNT(1) FROM information_schema.processlist WHERE user<>'system user' AND time >= ${SECONDS_TOO_LONG}"`
if [ ${QUERIES_RUNNING_TOO_LONG} -gt 0 ]
then
    KILLPROC_SQLSTMT="SELECT CONCAT('KILL QUERY ',id,';') KillQuery FROM information_schema.processlist WHERE user<>'system user' AND time >= ${SECONDS_TOO_LONG}"
    mysql -uroot -ppassword -ANe"${KILLPROC_SQLSTMT}" > /tmp/kill_log_queries.sql
    mysql -uroot -ppassword < /tmp/kill_log_queries.sql
fi

BTW Vous n'avez pas spécifié de myDB car j'ai explicitement lu dans information_schema.processlist un nom de table complet.

Voici une démonstration de ce que vous devriez voir. Pour cet exemple, je ferai écho à la commande KILL de tous les processus dont le temps> 20000 secondes:

[root@***** ~]# mysql `lwdba_connect` -ANe"SELECT GROUP_CONCAT('KILL ',id,'; ' SEPARATOR ' ') FROM information_schema.processlist WHERE time > 25000 AND user<>'system user';"
+----------------------------------------------------+
| KILL 180186;  KILL 180141;  KILL 176419;  KILL 3;  |
+----------------------------------------------------+
[root@***** ~]#

Je pratique cette technique depuis 5 ans. En fait, j'ai soumis cette réponse au DBA StackExchange l'année dernière et elle a été acceptée .

RolandoMySQLDBA
la source
Roland, pourriez-vous clarifier ces points: cette commande est-elle persistante ou doit-elle être exécutée fréquemment? Que faut-il remplacer dans la commande en supposant que l'utilisateur mysql est «root» et que le nom de la base de données est myDB? Merci
alfish
J'ai mis à jour ma réponse.
RolandoMySQLDBA
Merci, mais après avoir transformé votre dernière recette en script bash, j'obtiens: ERREUR 1064 (42000) à la ligne 1: vous avez une erreur dans votre syntaxe SQL; consultez le manuel qui correspond à la version de votre serveur MySQL pour la bonne syntaxe à utiliser près de '' à la ligne 1
alfish
Quelle commande SQL a généré cette erreur ???
RolandoMySQLDBA
Roland SF n'est pas un bac à sable. Mieux vaut tester avant de suggérer quelque chose comme solution. De plus, lorsque toutes les connexions seront saturées, comment mysql va exécuter votre procédure, en supposant qu'elle n'est pas défectueuse?
alfish
6

J'ai trouvé le code suivant coupé ici :

Mise à jour 2013-01-14: Il y avait une indication anonyme que cela est potentiellement dangereux et peut également tuer les processus de réplication. Utilisez donc à vos risques et périls:

mysql -e 'show processlist\G' |\
egrep -b5 'Time: [0-9]{2,}' |\
grep 'Id:' |\
cut -d':' -f2 |\
sed 's/^ //' |\
while read id
do
  mysql -e "kill $id;"
done
Nils
la source
Quelle est la constante de temps dans l'extrait ci-dessus?
alfish
@alfish Je ne suis pas l'auteur - mais je dirais que c'est une expression régulière qui correspond à toutes les valeurs de temps qui ont au moins deux chiffres. Donc, l'hypothèse ici est que 10 est trop long.
Nils
1

MySQL 5.7 et versions ultérieures, vous pouvez utiliser la variable max_execution_time pour que cela se fasse automatiquement pour toutes les requêtes de lecture "SELECT".

Pawan Gaur
la source
0

Je n'essaierais pas les solutions bash si vous aimez la disponibilité!

Si vous avez accès au code, vous pouvez réellement définir le temps d'exécution maximal sur les instructions SELECT à l'aide de la méthode décrite ici :

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

Sinon, sur le serveur:

/programming/415905/how-to-set-a-maximum-execution-time-for-a-mysql-query

Installez pt-kill:

$ wget percona.com/get/pt-kill

Prenez un instantané de votre liste de processus:

$ mysql -u root -B -pmyreallyimportantpassword -e "show processlist;" > processlist.txt

Testez pt-kill sur l'instantané:

$ ./pt-kill --test-matching processlist.txt --busy-time 45 --kill-busy-commands 'Execute' --victims all --print
# 2019-02-25T17:34:37 KILL 45710302 (Execute 374 sec) SELECT\n\tCOUNT(DISTINCT(LP.sessionId))\nFROM lp_traffic LP\nINNER JOIN orders O ON O.orderId = LP.order
# 2019-02-25T17:34:37 KILL 45713515 (Execute 67 sec) SELECT \n\tCOUNT(DISTINCT(CASE WHEN T.response = 'SUCCESS' AND T.isVoid = 0 AND (T.txnType IN

Assurez-vous que les règles du match conviennent à votre cas. Celles ci-dessus tueront toutes les instructions Execute en 45 secondes. Une fois que vous êtes sûr, modifiez et exécutez cette commande pour exécuter l'instruction à un intervalle de 10 secondes:

$ ./pt-kill -u root -p myreallyimportantpassword --busy-time 45 --kill-busy-commands 'Execute' --victims all --interval 10 --kill
Geoff_Clapp
la source