Obtention de «Délai d'attente verrouillé dépassé; essayez de redémarrer la transaction "même si je n'utilise pas de transaction

267

J'exécute l' UPDATEinstruction MySQL suivante :

mysql> update customer set account_import_id = 1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

Je n'utilise pas de transaction, alors pourquoi aurais-je cette erreur? J'ai même essayé de redémarrer mon serveur MySQL et cela n'a pas aidé.

Le tableau comporte 406 733 lignes.

Jason Swett
la source

Réponses:

211

Vous utilisez une transaction; autocommit ne désactive pas les transactions, il les fait simplement valider automatiquement à la fin de l'instruction.

Ce qui se passe, c'est qu'un autre thread maintient un verrou d'enregistrement sur un enregistrement (vous mettez à jour chaque enregistrement de la table!) Depuis trop longtemps, et votre thread est expiré.

Vous pouvez voir plus de détails sur l'événement en émettant un

SHOW ENGINE INNODB STATUS

après l'événement (dans l' sqléditeur). Idéalement, faites-le sur une machine d'essai silencieuse.

MarkR
la source
1
Existe-t-il un moyen d'enregistrer la sortie dans un fichier? J'ai essayé SHOW ENGINE INNODB STATUS \ G> innodb_stat.txt mais ne fonctionne pas.
yantaq
14
Depuis la ligne de commande: mysql [insérer les informations d'identification] -e "AFFICHER LE STATUT INNODB DU MOTEUR \ G"> innodb_stat.txt
VenerableAgents
si de nombreux threads (ou processus) mysql sont occupés, par exemple, certaines requêtes nécessitent un temps très long, vous devez donc attendre que certaines processsoient inactives. Si c'est le cas, vous pourriez obtenir cette erreur. Ai-je raison?
zhuguowei
6
L'exécution de plusieurs (2+) requêtes UPDATE sur la même ligne au cours d'une même transaction provoquera également cette erreur.
Okneloper
Pour ceux qui utilisent Python MySQL Connector, utilisez connection.commit()pour valider le INSERTou UPDATEque vous venez de parcourir.
AER du
334

Comment forcer le déverrouillage pour les tables verrouillées dans MySQL:

Le fait de casser des verrous comme celui-ci peut empêcher l' atomicité de la base de données sur les instructions sql à l'origine du verrou.

C'est hackish, et la bonne solution est de corriger votre application qui a provoqué les verrous. Cependant, lorsque les dollars sont en jeu, un coup de pied rapide fera bouger les choses à nouveau.

1) Entrez dans MySQL

mysql -u your_user -p

2) Voyons la liste des tables verrouillées

mysql> show open tables where in_use>0;

3) Voyons la liste des processus en cours, l'un d'eux verrouille votre table (s)

mysql> show processlist;

4) Tuez l'un de ces processus

mysql> kill <put_process_id_here>;
Eric Leschinski
la source
14
C'est dangereux et hackish. Une solution appropriée consiste à corriger votre application.
Zenexer
71
Non-sens, cela vous permet d'annuler un messup, puis de corriger l'application. Si je pouvais donner à ce gars 100 votes pour ce problème que je devais résoudre MAINTENANT, je le ferais.
Lizardx
7
Je suis d'accord avec Lizardx. C'était une solution très utile dans la situation que je n'ai pas eu le privilège d'appeler SHOW ENGINE INNODB STATUS
Travis Schneeberger
8
Comment est-il dangereux de tuer une requête de longue durée de cette manière ? Le client appelant recevra simplement une erreur.
Xeoncross
7
@EricLeschinski Je comprends votre argument, mais je dois vous demander pourquoi dans le monde utiliseriez-vous une base de données bâclée comme MySQL sur un système vital ?
Xeoncross
96
mysql> set innodb_lock_wait_timeout=100

Query OK, 0 rows affected (0.02 sec)

mysql> show variables like 'innodb_lock_wait_timeout';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_lock_wait_timeout | 100   |
+--------------------------+-------+

Déclenchez à nouveau le verrou. Vous avez 100 secondes pour émettre un SHOW ENGINE INNODB STATUS\Gdans la base de données et voir quelle autre transaction verrouille la vôtre.

veen
la source
8
Cette réponse n'explique pas pourquoi le demandeur obtient son erreur. Pourriez-vous expliquer pourquoi, en plus de simplement donner la réponse?
Traîneau du
5
+1 bien que cela ne réponde pas directement à la question, pour moi, c'est une bonne référence pour contourner ce problème.
Jossef Harush
1
@ArtB dev.mysql.com/doc/innodb/1.1/en/… En substance, l'OP reçoit l'erreur car un verrou a été appelé sur la table et le temps écoulé avant la fin de la transaction a dépassé la lock_wait_timeoutvaleur
fyrye
70

Vérifiez si votre base de données est bien réglée. Surtout l'isolement des transactions. Ce n'est pas une bonne idée d'augmenter la variable innodb_lock_wait_timeout.

Vérifiez le niveau d'isolement des transactions de votre base de données dans le CLI mysql:

mysql> SELECT @@GLOBAL.transaction_isolation, @@transaction_isolation, @@session.transaction_isolation;
+-----------------------+-----------------+------------------------+
| @@GLOBAL.tx_isolation | @@tx_isolation  | @@session.tx_isolation |
+-----------------------+-----------------+------------------------+
| REPEATABLE-READ       | REPEATABLE-READ | REPEATABLE-READ        |
+-----------------------+-----------------+------------------------+
1 row in set (0.00 sec)

Vous pouvez obtenir des améliorations en changeant le niveau d'isolation, utilisez l'oracle comme READ COMMITTED au lieu de REPEATABLE READ (InnoDB Defaults)

mysql> SET tx_isolation = 'READ-COMMITTED';
Query OK, 0 rows affected (0.00 sec)

mysql> SET GLOBAL tx_isolation = 'READ-COMMITTED';
Query OK, 0 rows affected (0.00 sec)

mysql> 

Essayez également d'utiliser SELECT FOR UPDATE uniquement si nécessaire.

saisyukusanagi
la source
1
Il s'agit d'une excellente solution pour les problèmes de verrouillage.
renouvelé le
3
Fonctionne très bien pour moi, la version my.cnf est [mysqld] transaction-isolation = READ-COMMITTED
Benoit Gauthier
Juste une note: MySQL 8 a renommé tx_isolationvariable en transaction_isolation.
xonya
Cependant, il faut prendre soin de savoir dans quoi vous vous engagez lorsque vous passez de l'isolement en lecture à LECTURE ENGAGÉE. Vous pouvez vous retrouver avec des données sales que vous voudrez peut-être éviter. Wikipedia Isoloation
huggie
27

Aucune des solutions suggérées n'a fonctionné pour moi mais cela a fonctionné.

Quelque chose bloque l'exécution de la requête. Une autre requête est probablement mise à jour, insérée ou supprimée d'une des tables de votre requête. Vous devez découvrir ce que c'est:

SHOW PROCESSLIST;

Une fois que vous avez localisé le processus de blocage, recherchez-le idet exécutez:

KILL {id};

Relancez votre requête initiale.

BassMHL
la source
J'ai accidentellement tué tous les processus répertoriés avec SHOW PROCESSLIST; Maintenant, j'obtiens 500 erreurs dans phpmyadmin. Cette erreur de 500 est-elle liée à la suppression de ces processus? Si oui, comment puis-je le redémarrer.
Dashrath
Je peux voir ce que vous décrivez, mais tuer le processus ne fonctionne pas. La commande du processus est «tuée», mais elle reste dans la liste des processus.
Torsten
12

100% avec ce que MarkR a dit. autocommit fait de chaque instruction une transaction d'une instruction.

SHOW ENGINE INNODB STATUSdevrait vous donner quelques indices sur la raison de l'impasse. Jetez également un œil à votre journal des requêtes lentes pour voir ce qui est en train d'interroger la table et essayez de supprimer tout ce qui fait un scan complet de la table. Le verrouillage au niveau des lignes fonctionne bien, mais pas lorsque vous essayez de verrouiller toutes les lignes!

James C
la source
5

Pouvez-vous mettre à jour tout autre enregistrement de cette table ou cette table est-elle fortement utilisée? Ce que je pense, c'est que pendant qu'il tente d'acquérir un verrou dont il a besoin pour mettre à jour cet enregistrement, le délai d'expiration défini a expiré. Vous pourrez peut-être augmenter le temps qui peut vous aider.

John Kane
la source
3
peut-être innodb_lock_wait_timeoutdansmy.cnf
obligé le
1
J'ai défini dans my.cnf: <br/> innodb_lock_wait_timeout = 120 <br/> La valeur par défaut est 50 pour mysql 5.5. Après ce changement, je n'ai pas pu voir ce problème dans mes tests unitaires! Cela s'est produit après le passage de proxool à tomcat jdbc pool. Probablement en raison de plus de temps de transaction avec le pool tomcat?!
Champ
3

Le nombre de lignes n'est pas énorme ... Créez un index sur account_import_id si ce n'est pas la clé primaire.

CREATE INDEX idx_customer_account_import_id ON customer (account_import_id);
gladiateur
la source
OMG ... cela m'a juste sauvé. J'ai vissé royalement une base de données de production en supprimant un index et cela l'a corrigé. Merci.
Nick Gotch
2

Si vous venez de tuer une grosse requête, cela prendra du temps rollback. Si vous émettez une autre requête avant la fin de la requête supprimée, vous pouvez obtenir une erreur de délai d'expiration du verrouillage. C'est ce qui m'est arrivé. La solution était d'attendre un peu.

Détails:

J'avais émis une requête DELETE pour supprimer environ 900 000 sur environ 1 million de lignes.

J'ai exécuté cela par erreur (ne supprime que 10% des lignes): DELETE FROM table WHERE MOD(id,10) = 0

Au lieu de cela (supprime 90% des lignes): DELETE FROM table WHERE MOD(id,10) != 0

Je voulais supprimer 90% des lignes, pas 10%. J'ai donc tué le processus dans la ligne de commande MySQL, sachant qu'il annulerait toutes les lignes qu'il avait supprimées jusqu'à présent.

Ensuite, j'ai exécuté la commande correcte immédiatement et j'ai reçu une lock timeout exceedederreur peu de temps après. J'ai réalisé que le verrou pourrait en fait être la rollbackrequête tuée qui se produit toujours en arrière-plan. J'ai donc attendu quelques secondes et relancé la requête.

Buttle Butkus
la source
1

Assurez-vous que les tables de base de données utilisent le moteur de stockage InnoDB et le niveau d'isolement des transactions READ-COMMITTED.

Vous pouvez le vérifier par SELECT @@ GLOBAL.tx_isolation, @@ tx_isolation; sur la console mysql.

S'il n'est pas configuré pour être LECTURE ENGAGÉE, vous devez le définir. Assurez-vous avant de le définir que vous avez des privilèges SUPER dans mysql.

Vous pouvez obtenir de l'aide sur http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html .

En réglant cela, je pense que votre problème sera résolu.


Vous pouvez également vérifier que vous n'essayez pas de mettre à jour cela dans deux processus à la fois. Les utilisateurs (@tala) ont rencontré des messages d'erreur similaires dans ce contexte, peut-être vérifier que ...

Ravi Chhatrala
la source
1

Je viens de Google et je voulais juste ajouter la solution qui fonctionnait pour moi. Mon problème était que j'essayais de supprimer les enregistrements d'une énorme table qui avait beaucoup de FK en cascade, donc j'ai eu la même erreur que l'OP.

J'ai désactivé le autocommitpuis cela a fonctionné simplement en ajoutantCOMMIT à la fin de la phrase SQL. Pour autant que je sache, cela libère le tampon bit par bit au lieu d'attendre à la fin de la commande.

Pour conserver l'exemple du PO, cela aurait dû fonctionner:

mysql> set autocommit=0;

mysql> update customer set account_import_id = 1; commit;

N'oubliez pas de réactiver le autocommitsi vous souhaitez laisser la configuration MySQL comme avant.

mysql> set autocommit=1;

Kamae
la source
0

Tard dans la soirée (comme d'habitude) mais mon problème était le fait que j'ai écrit du mauvais SQL (étant un novice) et que plusieurs processus avaient un verrou sur le (s) enregistrement (s) <- je ne suis pas sûr du verbiage approprié. J'ai fini par devoir simplement: SHOW PROCESSLISTpuis tuer les identifiants en utilisantKILL <id>

Smitty
la source
0

Ce genre de chose m'est arrivé lorsque j'utilisais la sortie de construction de langage php; au milieu de la transaction. Ensuite, cette transaction "se bloque" et vous devez tuer le processus mysql (décrit ci-dessus avec processlist;)

TomoMiha
la source
0

Dans mon cas, j'exécutais une requête anormale pour corriger les données. Si vous verrouillez les tables dans votre requête, vous n'aurez pas à gérer le délai de verrouillage:

LOCK TABLES `customer` WRITE;
update customer set account_import_id = 1;
UNLOCK TABLES;

Ce n'est probablement pas une bonne idée pour une utilisation normale.

Pour plus d'informations, voir: MySQL 8.0 Reference Manual

Jeff Luyet
la source
0

J'ai rencontré 2 connexions Doctrine DBAL, dont une non-transactionnelle (pour les journaux importants), elles sont destinées à fonctionner en parallèle sans dépendre l'une de l'autre.

CodeExecution(
    TransactionConnectionQuery()
    TransactionlessConnectionQuery()
)

Mes tests d'intégration ont été enveloppés dans des transactions pour la restauration des données après un test très poussé.

beginTransaction()
CodeExecution(
    TransactionConnectionQuery()
    TransactionlessConnectionQuery() // CONFLICT
)
rollBack()

Ma solution a été de désactiver la transaction d'encapsulation dans ces tests et de réinitialiser les données db d'une autre manière.

Fabian Picone
la source
-4

J'ai eu la même erreur, même si je ne mettais à jour qu'une table avec une entrée, mais après avoir redémarré mysql, elle a été résolue.

tubostique
la source