En mis registros de errores de producción ocasionalmente veo:
SQLSTATE [HY000]: Error general: 1205 Tiempo de espera de bloqueo excedido; intente reiniciar la transacción
Sé qué consulta está intentando acceder a la base de datos en ese momento, pero ¿hay alguna manera de averiguar qué consulta tenía el bloqueo en ese momento preciso?
Respuestas:
Lo que delata esto es la palabra transacción . Es evidente por la declaración que la consulta intentaba cambiar al menos una fila en una o más tablas InnoDB.
Como conoce la consulta, todas las tablas a las que se accede son candidatos para ser el culpable.
A partir de ahí, deberías poder correr
SHOW ENGINE INNODB STATUS\G
Debería poder ver las tablas afectadas
Obtiene todo tipo de información adicional de bloqueo y mutex.
Aquí hay una muestra de uno de mis clientes:
Debería considerar aumentar el valor de tiempo de espera de bloqueo para InnoDB configurando innodb_lock_wait_timeout , el valor predeterminado es 50 segundos
Puede establecerlo en un valor más alto de forma
/etc/my.cnf
permanente con esta líneay reinicie mysql. Si no puede reiniciar mysql en este momento, ejecute esto:
También puede configurarlo para la duración de su sesión
seguido de su consulta
fuente
innodb_lock_wait_timeout
variable solo se puede configurar al iniciar el servidor. Para InnoDB Plugin, se puede configurar al inicio o cambiarse en tiempo de ejecución, y tiene valores globales y de sesión.SQL Error (1064): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '\G' at line 1
SET GLOBAL innodb_lock_wait_timeout = 120;
volver a ejecutarlo . Si/etc/my.cnf
tiene la opción,innodb_lock_wait_timeout
está configurado para usted. No todos tienen el privilegio SUPER de cambiarlo globalmente para todos los demás ( dev.mysql.com/doc/refman/5.6/en/… )Como alguien mencionó en uno de los muchos subprocesos SO relacionados con este problema: ¡A veces el proceso que ha bloqueado la tabla aparece como inactivo en la lista de procesos! Me estaba arrancando el pelo hasta que maté todos los hilos dormidos que estaban abiertos en la base de datos en cuestión (ninguno estaba activo en ese momento). Eso finalmente desbloqueó la tabla y dejó que se ejecutara la consulta de actualización.
El comentarista dijo algo parecido a "A veces un hilo de MySQL bloquea una mesa, luego duerme mientras espera que suceda algo no relacionado con MySQL".
Después de volver a revisar el
show engine innodb status
registro (una vez que rastreé al cliente responsable del bloqueo), noté que el hilo atascado en cuestión estaba en la parte inferior de la lista de transacciones, debajo de las consultas activas que estaban a punto de error fuera debido a la cerradura congelada:(no estoy seguro si el mensaje "Vista de lectura de Trx" está relacionado con el bloqueo congelado, pero a diferencia de las otras transacciones activas, esta no aparece con la consulta que se emitió y en su lugar afirma que la transacción se está "limpiando", pero tiene múltiples bloqueos de fila)
La moraleja de la historia es que una transacción puede estar activa aunque el hilo esté inactivo .
fuente
show processlist;
para mostrar una lista exhaustiva de los procesos que se ejecutan actualmente, lo cual es bueno porque es una versión condensada deshow engine innodb status\g
. Además, si su base de datos está en una instancia de Amazon RDS, puede usarlaCALL mysql.rds_kill(<thread_id>);
para eliminar subprocesos. Creo que tiene permisos más altos, porque me permitió matar más procesos que los simpleskill <thread_id>;
: tenga en cuenta que estos deberían ejecutarse dentro de MySQL CLIDebido a la popularidad de MySQL, no es de extrañar que se haya excedido el tiempo de espera de bloqueo; Intentar reiniciar la excepción de transacción recibe tanta atención en SO.
Cuanta más contención tenga, mayor será la posibilidad de puntos muertos, que un motor de DB resolverá al agotar el tiempo de una de las transacciones bloqueadas. Además, las transacciones de larga duración que han modificado (por ejemplo,
UPDATE
oDELETE
) una gran cantidad de entradas (que toman bloqueos para evitar anomalías de escritura sucia como se explica en el libro de Persistencia Java de alto rendimiento ) tienen más probabilidades de generar conflictos con otras transacciones.Aunque InnoDB MVCC, aún puede solicitar bloqueos explícitos utilizando la
FOR UPDATE
cláusula . Sin embargo, a diferencia de otras bases de datos populares (Oracle, MSSQL, PostgreSQL, DB2), MySQL usaREPEATABLE_READ
como nivel de aislamiento predeterminado .Ahora, los bloqueos que adquirió (ya sea modificando filas o utilizando bloqueo explícito), se mantienen durante la transacción que se ejecuta actualmente. Si quieres una buena explicación de la diferencia entre
REPEATABLE_READ
yREAD COMMITTED
en cuanto a la fijación, por favor lea este artículo Percona .Por lo tanto: cuanto más restrictivo sea el nivel de aislamiento (
REPEATABLE_READ
,SERIALIZABLE
) mayor será la posibilidad de un punto muerto. Esto no es un problema "per se", es una compensación.Puede obtener muy buenos resultados
READ_COMMITED
, ya que necesita prevención de actualizaciones perdidas a nivel de aplicación cuando utiliza transacciones lógicas que abarcan múltiples solicitudes HTTP. El enfoque de bloqueo optimista apunta a las actualizaciones perdidas que pueden ocurrir incluso si usa elSERIALIZABLE
nivel de aislamiento mientras reduce la contención de bloqueo al permitirle usarREAD_COMMITED
.fuente
Para el registro, la excepción de tiempo de espera de bloqueo también ocurre cuando hay un punto muerto y MySQL no puede detectarlo, por lo que simplemente se agota el tiempo de espera. Otra razón podría ser una consulta de ejecución extremadamente larga, que es más fácil de resolver / reparar, sin embargo, y no describiré este caso aquí.
MySQL generalmente puede lidiar con puntos muertos si se construyen "correctamente" dentro de dos transacciones. MySQL luego simplemente mata / revierte la transacción que posee menos bloqueos (es menos importante ya que afectará menos filas) y deja que la otra termine.
Ahora, supongamos que hay dos procesos A y B y 3 transacciones:
Esta es una configuración muy desafortunada porque MySQL no puede ver que hay un punto muerto (abarcado en 3 transacciones). Entonces, lo que hace MySQL es ... ¡nada! Simplemente espera, ya que no sabe qué hacer. Espera hasta que el primer bloqueo adquirido exceda el tiempo de espera (Proceso A, Transacción 1: Bloqueos X), luego esto desbloqueará el Bloqueo X, que desbloquea la Transacción 2, etc.
El arte es descubrir qué (qué consulta) causa el primer bloqueo (Bloqueo X). Podrá ver fácilmente (
show engine innodb status
) que la Transacción 3 espera la Transacción 2, pero no verá qué transacción está esperando la Transacción 2 (Transacción 1). MySQL no imprimirá ningún bloqueo o consulta asociada con la Transacción 1. La única pista será que en la parte inferior de la lista de transacciones (de lashow engine innodb status
copia impresa), verá que la Transacción 1 aparentemente no hace nada (pero de hecho espera que la Transacción 3 terminar).Aquí se describe la técnica sobre cómo encontrar qué consulta SQL hace que se otorgue el bloqueo (Bloqueo X) para una transacción determinada que está esperando
Tracking MySQL query history in long running transactions
Si se pregunta cuál es el proceso y la transacción exactamente en el ejemplo. El proceso es un proceso PHP. Transacción es una transacción definida por innodb-trx-table . En mi caso, tenía dos procesos PHP, en cada uno comencé una transacción manualmente. La parte interesante fue que, aunque comencé una transacción en un proceso, MySQL utilizó internamente dos transacciones separadas (no tengo idea de por qué, tal vez algunos desarrolladores de MySQL puedan explicarlo).
MySQL está administrando sus propias transacciones internamente y decidió (en mi caso) usar dos transacciones para manejar todas las solicitudes SQL provenientes del proceso PHP (Proceso A). La afirmación de que la Transacción 1 está esperando que finalice la Transacción 3 es una cuestión interna de MySQL. MySQL "sabía" que la Transacción 1 y la Transacción 3 en realidad se instanciaron como parte de una solicitud de "transacción" (del Proceso A). Ahora toda la "transacción" fue bloqueada porque la Transacción 3 (una subparte de "transacción") fue bloqueada. Debido a que "transacción" no pudo finalizar la transacción 1 (también una subparte de la "transacción") también se marcó como no finalizada. Esto es lo que quise decir con "la transacción 1 espera a que finalice la transacción 3".
fuente
El gran problema con esta excepción es que generalmente no es reproducible en un entorno de prueba y no estamos para ejecutar el estado del motor innodb cuando ocurre en prod. Entonces, en uno de los proyectos, puse el código a continuación en un bloque catch para esta excepción. Eso me ayudó a detectar el estado del motor cuando ocurrió la excepción. Eso ayudó mucho.
fuente
Eche un vistazo a la página de manual de la
pt-deadlock-logger
utilidad :Extrae información de lo
engine innodb status
mencionado anteriormente y también se puede usar para crear unadaemon
que se ejecuta cada 30 segundos.fuente
Extrapolando la respuesta de Rolando anterior, estos son los que están bloqueando su consulta:
Si necesita ejecutar su consulta y no puede esperar a que se ejecuten los demás, elimínelos utilizando el ID de hilo de MySQL:
(desde mysql, no el shell, obviamente)
Tienes que encontrar las ID de hilo en:
comando y averiguar cuál es el que está bloqueando la base de datos.
fuente
5341773
? No veo lo que distingue a uno de los otros.Esto es lo que finalmente tuve que hacer para averiguar qué "otra consulta" causó el problema del tiempo de espera de bloqueo. En el código de la aplicación, rastreamos todas las llamadas a la base de datos pendientes en un hilo separado dedicado a esta tarea. Si cualquier llamada a la base de datos tarda más de N segundos (para nosotros son 30 segundos), registramos:
Con lo anterior, pudimos identificar consultas concurrentes que bloquearon las filas que causaron el punto muerto. En mi caso, fueron declaraciones como las
INSERT ... SELECT
que, a diferencia de los SELECT simples, bloquean las filas subyacentes. Luego puede reorganizar el código o usar un aislamiento de transacción diferente como lectura no confirmada.¡Buena suerte!
fuente
Puedes usar:
que enumerará todas las conexiones en MySQL y el estado actual de la conexión, así como la consulta que se está ejecutando. También hay una variante más corta
show processlist;
que muestra la consulta truncada, así como las estadísticas de conexión.fuente
Si está utilizando JDBC, entonces tiene la opción
includeInnodbStatusInDeadlockExceptions = true
https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-configuration-properties.html
fuente
Active MySQL general.log (uso intensivo de disco) y use mysql_analyse_general_log.pl para extraer transacciones de larga ejecución, por ejemplo con:
Desactivar general.log después de eso.
fuente