¿Cómo matar automáticamente consultas MySQL lentas después de N segundos?

16

Estoy buscando un script bash bien probado (o una solución alternativa) para hacerlo, a fin de evitar que se agote max_connection. Sé que está luchando contra los síntomas, pero realmente necesito ese script como una solución a corto plazo.

Alfish
fuente
¿Qué versión de MySQL está ejecutando?
RolandoMySQLDBA
la versión de mysql es 5.5
alfish

Respuestas:

21

echa un vistazo al comando pt-kill del kit de herramientas de percona .

y ... comience a monitorear su sistema: munin , cactus con mejores plantillas de cactus para mysql , cualquier cosa para que tenga una idea de lo que está sucediendo. el registro de consultas lentas de mysql también será una buena idea.

pQd
fuente
Gracias por las sugerencias, pero realmente busque un script "único".
alfish
55
pt-kill está muy bien probado y resuelve su problema exacto. Tarda unos 10 minutos en descubrir los parámetros de la línea de comandos y ejecutarlo. ¿Qué más quieres?
Aaron Brown
12

Si tiene MySQL 5.1 donde la lista de procesos está en INFORMATION_SCHEMA, puede hacer esto para generar los comandos KILL QUERY en masa desde el cliente mysql para consultas que se ejecutan más de 20 minutos (1200 segundos):

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

Puede hacer cláusulas WHERE en el campo INFO para buscar una consulta específica, el campo TIME en consultas de ejecución prolongada o el campo DB en una base de datos específica.

Si es root @ localhost, debe tener todos los privilegios para ejecutar esto de la siguiente manera

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

Puede crontab esto de la siguiente manera:

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

Aquí hay otra variación:

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

Por cierto, no tiene que especificar un myDB ya que leí explícitamente information_schema.processlist como un nombre de tabla completamente calificado.

Aquí hay una demostración de lo que debería ver. Para este ejemplo, haré eco del comando KILL de todos los procesos cuyo tiempo> 20000 segundos:

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

He estado haciendo esta técnica durante los últimos 5 años. De hecho, envié esta respuesta al DBA StackExchange el año pasado y fue aceptada .

RolandoMySQLDBA
fuente
Roland, ¿podría aclarar esto? ¿Es este comando persistente o necesita ejecutarse con frecuencia? ¿Qué se debe sustituir en el comando suponiendo que el usuario mysql es 'root' y el nombre de la base de datos es myDB? Gracias
alfish
Actualicé mi respuesta.
RolandoMySQLDBA
Gracias, pero después de convertir su última receta en un script bash, obtengo: ERROR 1064 (42000) en la línea 1: tiene un error en su sintaxis SQL; consulte el manual que corresponde a la versión de su servidor MySQL para conocer la sintaxis correcta para usar cerca de '' en la línea 1
alfish
¿Qué comando SQL generó ese error?
RolandoMySQLDBA
Roland SF no es una caja de arena. Es mejor probar antes de sugerir algo como solución. Además, cuando todas las conexiones están saturadas, ¿cómo va a ejecutar mysql su procedimiento, suponiendo que no sea defectuoso?
alfish
6

Encontré el siguiente código recortado aquí :

Actualización 2013-01-14: Hubo una sugerencia anónima de que esto es potencialmente peligroso y también puede matar los procesos de replicación. Así que use bajo su propio riesgo:

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
fuente
¿Cuál es la constante de tiempo en el fragmento de arriba?
alfish
@alfish No soy el autor, pero diría que esta es una expresión regular que coincide con todos los valores de tiempo que tienen al menos dos dígitos. Entonces, la suposición aquí es que 10 es demasiado largo.
Nils
1

MySQL 5.7 en adelante puede usar la variable max_execution_time para hacer esto automáticamente para todas las consultas de lectura "SELECCIONAR".

Pawan Gaur
fuente
0

¡No probaría las soluciones bash si te gusta el tiempo de actividad!

Si tiene acceso al código, puede establecer el tiempo de ejecución máximo en las instrucciones SELECT utilizando el método descrito aquí :

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

De lo contrario, en el servidor:

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

Instalar pt-kill:

$ wget percona.com/get/pt-kill

Tome una instantánea de su lista de procesos:

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

Prueba pt-kill en la instantánea:

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

Asegúrese de que las reglas del partido se adapten a su caso. Estos anteriores matarán todas las declaraciones de ejecución durante 45 segundos. Una vez que esté seguro, modifique y ejecute este comando para ejecutar la declaración en un intervalo de 10 segundos:

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