Wie kann ich langsame MySQL-Abfragen nach N Sekunden automatisch beenden?


16

Ich bin auf der Suche nach einem gut getesteten Bash-Skript (oder einer alternativen Lösung), um zu vermeiden, dass max_connection erschöpft ist. Ich weiß, dass es die Symptome bekämpft, brauche aber wirklich ein solches Skript als kurzfristige Lösung.


Welche Version von MySQL laufen Sie?
RolandoMySQLDBA

Die MySQL-Version ist 5.5
alfish

Antworten:


21

Besuche pt-Kill - Befehl aus dem Percona Toolkit .

und ... fangen Sie an, Ihr System zu überwachen - Munin , Kakteen mit besseren Kakteenvorlagen für MySQL , alles, damit Sie eine Vorstellung davon bekommen, was los ist. Das Protokollieren langsamer mysql-Abfragen ist ebenfalls eine gute Idee.


Vielen Dank für die Vorschläge, aber wirklich nach einem "einmaligen" Skript suchen.
Alfish

5
pt-kill ist sehr gut getestet und löst Ihr genaues Problem. Es dauert ungefähr 10 Minuten, um die Befehlszeilenparameter zu ermitteln und zum Laufen zu bringen. Was willst du noch?
Aaron Brown

12

Wenn Sie MySQL 5.1 haben, dessen Prozessliste sich in der INFORMATION_SCHEMA befindet, können Sie dies tun, um die KILL QUERY-Befehle in großen Mengen aus dem mysql-Client für Abfragen zu generieren, die länger als 20 Minuten (1200 Sekunden) ausgeführt werden:

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

Sie können WHERE-Klauseln für das INFO-Feld verwenden, um nach einer bestimmten Abfrage zu suchen, das TIME-Feld für lang laufende Abfragen oder das DB-Feld für eine bestimmte Datenbank.

Wenn Sie root @ localhost sind, sollten Sie über die folgenden Berechtigungen verfügen, um dies auszuführen

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

Sie können dies wie folgt überprüfen:

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

Hier ist eine andere 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

Übrigens: Sie haben keine myDB angegeben, da ich explizit aus information_schema.processlist als vollständig qualifizierten Tabellennamen gelesen habe.

Hier ist eine Demonstration dessen, was Sie sehen sollten. In diesem Beispiel werde ich den KILL-Befehl aller Prozesse wiederholen, deren Zeit> 20000 Sekunden ist:

[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@***** ~]#

Ich mache diese Technik seit 5 Jahren. Tatsächlich habe ich diese Antwort letztes Jahr beim DBA StackExchange eingereicht und sie wurde akzeptiert .


Roland, könnten Sie bitte Folgendes klären: Ist dieser Befehl dauerhaft oder muss er häufig ausgeführt werden? Was sollte im Befehl ersetzt werden, wenn der Benutzer mysql 'root' und der Datenbankname myDB ist? Danke
alfish

Ich habe meine Antwort aktualisiert.
RolandoMySQLDBA

Danke, aber nachdem ich Ihr letztes Rezept in ein Bash-Skript umgewandelt habe, erhalte ich: FEHLER 1064 (42000) in Zeile 1: Sie haben einen Fehler in Ihrer SQL-Syntax. Überprüfen Sie das Handbuch, das Ihrer MySQL-Serverversion entspricht, auf die richtige Syntax für near '' in Zeile 1
alfish

Welcher SQL-Befehl hat diesen Fehler erzeugt ???
RolandoMySQLDBA

Roland SF ist kein Sandkasten. Besser testen, bevor Sie etwas als Lösung vorschlagen. Darüber hinaus, wenn alle Verbindungen gesättigt sind, wie wird MySQL Ihre Prozedur ausführen, vorausgesetzt, es ist nicht fehlerhaft?
Alfish

6

Ich habe hier den folgenden Code gefunden :

Update 2013-01-14: Es gab einen anonymen Hinweis darauf, dass dies möglicherweise gefährlich ist und auch Replikationsprozesse zum Erliegen bringen kann. So verwenden Sie auf eigenes Risiko:

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

Was ist die Zeitkonstante im obigen Snippet?
alfish

@alfish Ich bin nicht der Autor - aber ich würde sagen, dass dies ein regulärer Ausdruck ist, der mit allen Zeitwerten übereinstimmt, die mindestens zwei Ziffern haben. Die Annahme hier ist also, dass 10 zu lang ist.
Nils

1

Ab MySQL 5.7 können Sie die Variable max_execution_time verwenden, um dies automatisch für alle "SELECT" -Leseabfragen zu erledigen.


0

Ich würde keine bash-Lösungen ausprobieren, wenn Sie die Betriebszeit mögen!

Wenn Sie Zugriff auf den Code haben , können Sie tatsächlich die maximale Ausführungszeit auf SELECT - Anweisungen gesetzt mit der Methode skizziert hier :

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

Andernfalls auf dem Server:

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

Installieren Sie pt-kill:

$ wget percona.com/get/pt-kill

Machen Sie einen Schnappschuss Ihrer Prozessliste:

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

Testen Sie pt-kill auf dem Schnappschuss:

$ ./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

Stellen Sie sicher, dass die Spielregeln Ihrem Fall entsprechen. Diese oben genannten Befehle beenden alle Execute-Anweisungen innerhalb von 45 Sekunden. Wenn Sie sicher sind, ändern Sie diesen Befehl und führen Sie ihn aus, um die Anweisung im Abstand von 10 Sekunden auszuführen:

$ ./pt-kill -u root -p myreallyimportantpassword --busy-time 45 --kill-busy-commands 'Execute' --victims all --interval 10 --kill
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.