Correction du délai d'attente de verrouillage dépassé; essayez de redémarrer la transaction »pour une table Mysql« bloquée »?


131

À partir d'un script, j'ai envoyé une requête comme celle-ci des milliers de fois à ma base de données locale:

update some_table set some_column = some_value

J'ai oublié d'ajouter la partie where, donc la même colonne a été définie sur la même valeur pour toutes les lignes de la table et cela a été fait des milliers de fois et la colonne a été indexée, donc l'index correspondant a probablement été mis à jour trop de fois .

J'ai remarqué que quelque chose n'allait pas, car cela prenait trop de temps, alors j'ai tué le script. J'ai même redémarré mon ordinateur depuis lors, mais quelque chose est resté coincé dans le tableau, car les requêtes simples prennent beaucoup de temps à s'exécuter et lorsque j'essaie de supprimer l'index pertinent, cela échoue avec ce message:

Lock wait timeout exceeded; try restarting transaction

C'est une table innodb, donc la transaction bloquée est probablement implicite. Comment puis-je corriger cette table et en supprimer la transaction bloquée?


3
Quelle est la sortie de SHOW FULL PROCESSLIST?
Wolph

Il affiche uniquement la commande SHOW FULL PROCESSLIST, rien d'autre. C'est une base de données de développement local. Rien ne fonctionne dessus. J'ai reçu le message d'erreur «lock wait ..» sur la ligne de commande lorsque j'ai essayé de supprimer l'index à partir de là.
Tom

Dans ce cas, vous créez probablement 2 connexions distinctes dans différentes transactions qui doivent attendre l'une l'autre.
Wolph

Je n'ai créé aucune transaction par la suite. J'ai tué le script, redémarré la machine et me suis connecté à partir de la ligne de commande pour regarder autour de moi. Rien d'autre n'utilisait la base de données à l'exception du client de ligne de commande mysql, donc quelque chose devait être coincé dans la table.
Tom

Réponses:


143

J'ai eu un problème similaire et je l'ai résolu en vérifiant les threads en cours d'exécution. Pour voir les threads en cours d'exécution, utilisez la commande suivante dans l'interface de ligne de commande mysql:

SHOW PROCESSLIST;

Il peut également être envoyé depuis phpMyAdmin si vous n'avez pas accès à l'interface de ligne de commande mysql.
Cela affichera une liste de threads avec les identifiants correspondants et le temps d'exécution, afin que vous puissiez TUER les threads qui prennent trop de temps à s'exécuter. Dans phpMyAdmin, vous aurez un bouton pour arrêter les threads en utilisant KILL, si vous utilisez l'interface de ligne de commande, utilisez simplement la commande KILL suivie de l'ID de thread, comme dans l'exemple suivant:

KILL 115;

Cela mettra fin à la connexion pour le thread correspondant.


36
PRENDRE NOTE! Cela a été mentionné par quelqu'un sur 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! Je m'arrachais les cheveux jusqu'à ce que je tue tous les fils ouverts dans la base de données en question, endormis ou non. Cela a finalement déverrouillé la table et a laissé la requête de mise à jour s'exécuter. Le commentateur a mentionné quelque chose comme "Parfois, un thread MySQL verrouille une table, puis se met en veille en attendant que quelque chose qui ne soit pas lié à MySQL se produise."
Eric L.

(J'ai ajouté ma propre réponse pour étoffer ce fragment de pensée ici , sur une question connexe)
Eric L.

Cela m'a aidé. J'ai eu quelques threads en sommeil créés par la fonction de gestion / requête de base de données PHPStorm.
Ejaz le

Génial! J'ai eu un processus caché depuis 5 heures que j'ai pu identifier et tuer.
Aldo Paradiso

2
ce n'est pas une solution, pas plus que le ruban adhésif sur une plaie infectée n'est une solution. vous n'abordez pas le problème fondamental sous-jacent
user2914191

55

Vous pouvez vérifier les transactions en cours avec

SELECT * FROM `information_schema`.`innodb_trx` ORDER BY `trx_started`

Votre transaction doit être l'une des premières, car c'est la plus ancienne de la liste. Maintenant, prenez simplement la valeur de trx_mysql_thread_idet envoyez-lui la KILLcommande:

KILL 1234;

Si vous ne savez pas quelle transaction est la vôtre, répétez la première requête très souvent et voyez quelles transactions persistent.


Vous devrez peut-être utiliser un compte root pour exécuter ce SQL afin de voir quelle transaction empêche les autres d'accéder à la table
tom10271

40

Vérifier l'état InnoDB pour les verrous

SHOW ENGINE InnoDB STATUS;

Vérifier les tables ouvertes MySQL

SHOW OPEN TABLES WHERE In_use > 0;

Vérifier les transactions InnoDB en attente

SELECT * FROM `information_schema`.`innodb_trx` ORDER BY `trx_started`; 

Vérifier la dépendance de verrouillage - ce qui bloque quoi

SELECT * FROM `information_schema`.`innodb_locks`;

Après avoir étudié les résultats ci-dessus, vous devriez être en mesure de voir ce qui verrouille quoi.

La cause principale du problème peut également être dans votre code - veuillez vérifier les fonctions associées, en particulier pour les annotations si vous utilisez JPA comme Hibernate.

Par exemple, comme décrit ici , l'utilisation abusive de l'annotation suivante peut entraîner des verrous dans la base de données:

@Transactional(propagation = Propagation.REQUIRES_NEW) 

4
Merci beaucoup! L'exécution a SELECT * FROM information_schema.innodb_trx t JOIN information_schema.processlist p ON t.trx_mysql_thread_id = p.idrévélé le coupable: le thread de verrouillage provenait de mon adresse IP ... j'avais oublié de fermer une console de débogage que j'avais laissée au milieu d'une transaction ...
Elias Strehle

40

Cela a commencé à m'arriver lorsque la taille de ma base de données a augmenté et que je faisais beaucoup de transactions dessus.

La vérité est qu'il existe probablement un moyen d'optimiser vos requêtes ou votre base de données, mais essayez ces 2 requêtes pour un correctif de contournement.

Lance ça:

SET GLOBAL innodb_lock_wait_timeout = 5000; 

Et puis ceci:

SET innodb_lock_wait_timeout = 5000; 

2
Lien de référence: innodb_lock_wait_timeout
culix

1
J'exécute une base de données de 11 Go très occupée. C'est la seule chose qui a fonctionné pour moi, merci!
dongemus

N'oubliez pas de remplacer les valeurs par les valeurs précédentes si vous ne souhaitez pas les conserver pour toujours.
Ricardo Martins

Cela signifie que certains de mes utilisateurs auront une expérience plus lente, non?
Arnold Roa

9

Lorsque vous établissez une connexion pour une transaction, vous acquérez un verrou avant d'effectuer la transaction. Si vous ne parvenez pas à acquérir le verrou, vous essayez pendant un certain temps. Si le verrouillage ne peut toujours pas être obtenu, une erreur de dépassement du temps d'attente de verrouillage est levée. La raison pour laquelle vous ne pourrez pas acquérir un verrou est que vous ne fermez pas la connexion. Ainsi, lorsque vous essayez d'obtenir un verrou une deuxième fois, vous ne pourrez pas acquérir le verrou car votre connexion précédente est toujours ouverte et maintient le verrou.

Solution: fermez la connexion ou setAutoCommit(true)(selon votre conception) pour libérer le verrou.


7

Redémarrez MySQL, cela fonctionne bien.

MAIS attention, si une telle requête est bloquée, il y a un problème quelque part:

  • dans votre requête (caractère mal placé, produit cartésien, ...)
  • très nombreux enregistrements à éditer
  • jointures ou tests complexes (MD5, sous-chaînes LIKE %...%, etc.)
  • problème de structure de données
  • modèle de clé étrangère (verrouillage chaîne / boucle)
  • données mal indexées

Comme l'a dit @syedrakib, cela fonctionne mais ce n'est pas une solution durable pour la production.

Attention: le redémarrage peut affecter vos données avec un état incohérent.

Aussi, vous pouvez vérifier comment MySQL gère votre requête avec le mot-clé EXPLAIN et voir si quelque chose y est possible pour accélérer la requête (index, tests complexes, ...).


MERCI. vous n'avez pas résolu mon problème directement mais j'ai souffert de verrous de table et c'était tellement mauvais. C'est le seul message sur tout Internet, ce qui m'amène à l'idée de vérifier les clés étrangères. J'ai donc découvert que la clé de la clé primaire était 11 et les clés étrangères 10. Je ne sais pas comment cela pouvait arriver et pourquoi tout fonctionnait avant.
EscapeNetscape

@EscapeNetscape vous êtes les bienvenus, je suis heureux que cette petite réponse vous ait aidé :)
Benj

3

Accéder aux processus dans mysql.

Vous pouvez donc voir qu'il y a encore une tâche à accomplir.

Tuez le processus particulier ou attendez la fin du processus.


7
Cela résout le problème. Mais cela ne peut pas être une solution pour un serveur de production LIVE ... Comment pouvons-nous générer cette gestion de blocage? Ou comment pouvons-nous éviter cette impasse?
Rakib

1
Eh bien, il se trouve que même avec des requêtes mysql PARFAITEMENT bien formées et PARFAITEMENT échappées qui ne sont même pas écrites par le développeur mais par l'interface active-records du framework, des problèmes de délai d'attente de verrouillage peuvent TOUJOURS se produire. Donc, je n'écris pas une requête mysql appropriée est un facteur ici.
Rakib

1

J'ai rencontré le même problème avec une déclaration "mise à jour". Ma solution consistait simplement à parcourir les opérations disponibles dans phpMyAdmin pour la table. J'ai optimisé, vidé et défragmenté la table (pas dans cet ordre). Pas besoin de supprimer la table et de la restaurer à partir d'une sauvegarde pour moi. :)


1
Cela pourrait résoudre le problème. Mais devoir faire cela à chaque fois qu'une telle erreur se produit ne peut pas être une solution pour un serveur de production LIVE ... Comment pouvons-nous générer cette gestion de blocage? Ou comment pouvons-nous éviter cette impasse?
Rakib

1

J'ai eu le même problème. Je pense que c'était un problème de blocage avec SQL. Vous pouvez simplement forcer la fermeture du processus SQL à partir du Gestionnaire des tâches. Si cela ne résout pas le problème, redémarrez simplement votre ordinateur. Vous n'avez pas besoin de supprimer la table et de recharger les données.


Cela résout le problème. Mais cela ne peut pas être une solution pour un serveur de production LIVE ... Comment pouvons-nous générer cette gestion de blocage? Ou comment pouvons-nous éviter cette impasse?
Rakib

redémarrez Apache et ses services (ou du moins MySQL), pas besoin de redémarrer
Amjo

1

J'ai eu ce problème en essayant de supprimer un certain groupe d'enregistrements (en utilisant MS Access 2007 avec une connexion ODBC à MySQL sur un serveur Web). En règle générale, je supprimerais certains enregistrements de MySQL, puis les remplacerais par des enregistrements mis à jour (suppression en cascade de plusieurs enregistrements associés, cela simplifie la suppression de tous les enregistrements associés pour une seule suppression d'enregistrement).

J'ai essayé de parcourir les opérations disponibles dans phpMyAdmin pour la table (optimiser, vider, etc.), mais j'obtenais une autorisation nécessaire pour RELOAD erreur lorsque j'ai essayé de vider. Étant donné que ma base de données se trouve sur un serveur Web, je n'ai pas pu redémarrer la base de données. La restauration à partir d'une sauvegarde n'était pas une option.

J'ai essayé d'exécuter une requête de suppression pour ce groupe d'enregistrements sur l'accès mySQL de cPanel sur le Web. Vous avez le même message d'erreur.

Ma solution: j'ai utilisé le navigateur de requêtes MySQL gratuit de Sun (Oracle) (que j'avais précédemment installé sur mon ordinateur) et y ai exécuté la requête de suppression. Cela a fonctionné tout de suite, le problème a été résolu. J'ai ensuite pu à nouveau exécuter la fonction en utilisant le script Access en utilisant la connexion ODBC Access to MySQL.


-2

Corrigé.

Assurez-vous que vous n'avez pas d'insertion de type de données incompatible dans la requête. J'ai eu un problème où j'essayais des "données d'agent du navigateur utilisateur" VARCHAR(255)et un problème avec ce verrou lorsque je l'ai changé enTEXT(255) a corrigé.

Il s'agit donc très probablement d'une incompatibilité de type de données.


-151

J'ai résolu le problème en supprimant la table et en la restaurant à partir d'une sauvegarde.


20
Incidemment, cette réponse remporte l'honneur d'être la réponse «acceptée» la plus basse de tous les temps de SO! 🏆
ashleedawg

1
C'est aussi la réponse la moins bien notée en général alors
Bob Kerman le
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.