Dans mes journaux d'erreurs de production, je vois parfois:
SQLSTATE [HY000]: Erreur générale: 1205 Dépassement du délai d'attente de verrouillage; essayez de redémarrer la transaction
Je sais quelle requête essaie d'accéder à la base de données à ce moment, mais existe-t-il un moyen de savoir quelle requête avait le verrou à ce moment précis?
Réponses:
Ce qui donne cela, c'est le mot transaction . Il est évident par l'instruction que la requête tentait de modifier au moins une ligne dans une ou plusieurs tables InnoDB.
Puisque vous connaissez la requête, toutes les tables auxquelles vous accédez sont des candidats pour être le coupable.
De là, vous devriez pouvoir exécuter
SHOW ENGINE INNODB STATUS\G
Vous devriez pouvoir voir les tables affectées
Vous obtenez toutes sortes d'informations supplémentaires sur le verrouillage et les mutex.
Voici un échantillon d'un de mes clients:
Vous devriez envisager d'augmenter la valeur du délai d'attente de verrouillage pour InnoDB en définissant la valeur innodb_lock_wait_timeout , la valeur par défaut est 50 s
Vous pouvez le régler sur une valeur plus élevée en
/etc/my.cnf
permanence avec cette ligneet redémarrez mysql. Si vous ne pouvez pas redémarrer mysql à ce stade, exécutez ceci:
Vous pouvez également le régler pour la durée de votre session
suivi de votre requête
la source
innodb_lock_wait_timeout
variable ne peut être définie qu'au démarrage du serveur. Pour le plug-in InnoDB, il peut être défini au démarrage ou modifié au moment de l'exécution, et a des valeurs globales et de session.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;
. Si/etc/my.cnf
a l'option,innodb_lock_wait_timeout
est réglé pour vous. Tout le monde n'a pas le privilège SUPER de le changer globalement pour tout le monde ( dev.mysql.com/doc/refman/5.6/en/… )Comme quelqu'un l'a mentionné dans l'un des nombreux threads SO concernant ce problème: Parfois, le processus qui a verrouillé la table apparaît comme dormant dans la liste des processus! J'arrachais mes cheveux jusqu'à ce que je tue tous les fils dormants qui étaient ouverts dans la base de données en question (aucun n'était actif à l'époque). Cela a finalement déverrouillé la table et laissé la requête de mise à jour s'exécuter.
Le commentateur a dit quelque chose de semblable à «Parfois, un thread MySQL verrouille une table, puis se met en veille pendant qu'il attend que quelque chose non lié à MySQL se produise.
Après avoir réexaminé le
show engine innodb status
journal (une fois que j'ai retrouvé le client responsable du verrou), j'ai remarqué que le fil coincé en question était répertorié tout en bas de la liste des transactions, sous les requêtes actives qui étaient sur le point de générer une erreur. à cause du verrou gelé:(vous ne savez pas si le message "Trx read view" est lié au verrou gelé, mais contrairement aux autres transactions actives, celle-ci ne s'affiche pas avec la requête qui a été émise et prétend à la place que la transaction est en train de "nettoyer", mais a plusieurs verrous de ligne)
La morale de l'histoire est qu'une transaction peut être active même si le thread est en sommeil.
la source
show processlist;
pour afficher une liste exhaustive des processus en cours d'exécution, ce qui est bien car c'est une version condensée deshow engine innodb status\g
. De plus, si votre base de données se trouve sur une instance Amazon RDS, vous pouvez utiliserCALL mysql.rds_kill(<thread_id>);
pour tuer les threads. Il a des autorisations plus élevées, je pense, car cela m'a permis de tuer plus de processus que plainkill <thread_id>;
- notez que ceux-ci devraient être exécutés dans MySQL CLIEn raison de la popularité de MySQL, il n'est pas étonnant que le délai d'attente de verrouillage ait été dépassé; essayez de redémarrer la transaction exception de attire tellement l'attention sur SO.
Plus vous avez de conflits, plus les risques de blocages sont élevés, ce qu'un moteur de base de données résoudra en expirant l'une des transactions bloquées. En outre, les transactions de longue durée qui ont modifié (par exemple
UPDATE
ouDELETE
) un grand nombre d'entrées (qui prennent des verrous pour éviter les anomalies d'écriture incorrecte, comme expliqué dans la persistance Java haute performance livre de ) sont plus susceptibles de générer des conflits avec d'autres transactions.Bien qu'InnoDB MVCC, vous pouvez toujours demander des verrous explicites à l'aide de la
FOR UPDATE
clause . Cependant, contrairement à d'autres bases de données populaires (Oracle, MSSQL, PostgreSQL, DB2), MySQL utiliseREPEATABLE_READ
comme niveau d'isolement par défaut .Désormais, les verrous que vous avez acquis (en modifiant des lignes ou en utilisant un verrouillage explicite) sont conservés pendant la durée de la transaction en cours d'exécution. Si vous voulez une bonne explication de la différence entre
REPEATABLE_READ
etREAD COMMITTED
en ce qui concerne le verrouillage, veuillez lire cet article Percona .Par conséquent: plus le niveau d'isolement est restrictif (
REPEATABLE_READ
,SERIALIZABLE
) est plus les chances de blocage sont grandes. Ce n'est pas un problème "en soi", c'est un compromis.Vous pouvez obtenir de très bons résultats avec
READ_COMMITED
, car vous avez besoin d'une prévention des mises à jour perdues au niveau de l'application lorsque vous utilisez des transactions logiques qui s'étendent sur plusieurs requêtes HTTP. L' approche de verrouillage optimiste cible les mises à jour perdues qui peuvent se produire même si vous utilisez leSERIALIZABLE
niveau d'isolement tout en réduisant le conflit de verrouillage en vous permettant d'utiliserREAD_COMMITED
.la source
Pour mémoire, l'exception de délai d'attente de verrouillage se produit également lorsqu'il y a un blocage et que MySQL ne peut pas le détecter, il expire donc simplement. Une autre raison pourrait être une requête extrêmement longue, qui est cependant plus facile à résoudre / réparer, et je ne décrirai pas ce cas ici.
MySQL est généralement capable de gérer les blocages s'ils sont construits "correctement" en deux transactions. MySQL tue / annule ensuite la seule transaction qui possède moins de verrous (est moins importante car elle affectera moins de lignes) et laisse l'autre terminer.
Supposons maintenant qu'il existe deux processus A et B et 3 transactions:
C'est une configuration très malheureuse car MySQL ne peut pas voir qu'il y a un blocage (réparti sur 3 transactions). Donc ce que MySQL fait est ... rien! Il attend juste, car il ne sait pas quoi faire. Il attend que le premier verrou acquis dépasse le délai d'expiration (Traiter une transaction 1: verrouille X), puis cela débloquera le verrou X, ce qui déverrouille la transaction 2, etc.
L'art est de savoir ce qui (quelle requête) provoque le premier verrou (Lock X). Vous pourrez voir facilement (
show engine innodb status
) que la transaction 3 attend la transaction 2, mais vous ne verrez pas quelle transaction la transaction 2 attend (transaction 1). MySQL n'imprimera aucun verrou ou requête associé à la transaction 1. Le seul indice sera que tout en bas de la liste des transactions (dushow engine innodb status
impression), vous verrez la transaction 1 apparemment ne rien faire (mais en fait, attendre que la transaction 3 soit terminée). terminer).La technique pour savoir quelle requête SQL entraîne l'octroi du verrou (Lock X) pour une transaction en attente est décrite ici
Tracking MySQL query history in long running transactions
Si vous vous demandez quel est le processus et la transaction exactement dans l'exemple. Le processus est un processus PHP. La transaction est une transaction telle que définie par innodb-trx-table . Dans mon cas, j'avais deux processus PHP, dans chacun j'ai commencé une transaction manuellement. La partie intéressante était que même si j'ai commencé une transaction dans un processus, MySQL utilisait en interne deux transactions distinctes (je n'ai aucune idée pourquoi, peut-être que certains développeurs MySQL peuvent l'expliquer).
MySQL gère ses propres transactions en interne et a décidé (dans mon cas) d'utiliser deux transactions pour gérer toutes les requêtes SQL provenant du processus PHP (Process A). La déclaration selon laquelle la transaction 1 attend la fin de la transaction 3 est une chose interne à MySQL. MySQL "savait" que la Transaction 1 et la Transaction 3 étaient en fait instanciées dans le cadre d'une demande de "transaction" (du Processus A). Maintenant, la "transaction" entière a été bloquée car la transaction 3 (une sous-partie de "transaction") a été bloquée. Parce que "transaction" n'a pas pu terminer la transaction 1 (également une sous-partie de la "transaction") a également été marquée comme non terminée. C'est ce que je voulais dire par "Transaction 1 attend la fin de la Transaction 3".
la source
Le gros problème avec cette exception est que ce n'est généralement pas reproductible dans un environnement de test et nous ne sommes pas là pour exécuter le statut du moteur innodb quand cela se produit sur prod. Donc, dans l'un des projets, j'ai mis le code ci-dessous dans un bloc catch pour cette exception. Cela m'a aidé à saisir l'état du moteur lorsque l'exception s'est produite. Cela a beaucoup aidé.
la source
Jetez un œil à la page de manuel de l'
pt-deadlock-logger
utilitaire :Il extrait des informations de ce qui
engine innodb status
précède et peut également être utilisé pour créer undaemon
qui s'exécute toutes les 30 secondes.la source
Extrapolant de la réponse de Rolando ci-dessus, ce sont eux qui bloquent votre requête:
Si vous devez exécuter votre requête et ne pouvez pas attendre que les autres s'exécutent, tuez-les en utilisant l'ID de thread MySQL:
(depuis mysql, pas le shell, évidemment)
Vous devez trouver les ID de thread à partir de:
et déterminez lequel est celui qui bloque la base de données.
la source
5341773
? Je ne vois pas ce qui le distingue des autres.Voici ce que j'ai finalement dû faire pour déterminer quelle «autre requête» a causé le problème de délai d'attente de verrouillage. Dans le code d'application, nous suivons tous les appels de base de données en attente sur un thread distinct dédié à cette tâche. Si un appel DB prend plus de N secondes (pour nous, c'est 30 secondes), nous enregistrons:
Avec ce qui précède, nous avons pu identifier les requêtes simultanées qui ont verrouillé les lignes à l'origine de l'impasse. Dans mon cas, c'étaient des déclarations comme
INSERT ... SELECT
qui, contrairement aux SELECT simples, verrouillent les lignes sous-jacentes. Vous pouvez ensuite réorganiser le code ou utiliser une isolation de transaction différente comme la lecture non validée.Bonne chance!
la source
Vous pouvez utiliser:
qui listera toutes les connexions dans MySQL et l'état actuel de la connexion ainsi que la requête en cours d'exécution. Il existe également une variante plus courte
show processlist;
qui affiche la requête tronquée ainsi que les statistiques de connexion.la source
Si vous utilisez JDBC, vous avez l'option
includeInnodbStatusInDeadlockExceptions = true
https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-configuration-properties.html
la source
Activez MySQL general.log (disque) et utilisez mysql_analyse_general_log.pl pour extraire les transactions de longue durée, par exemple avec:
Désactivez general.log après cela.
la source