web-dev-qa-db-fra.com

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

J'exécute l'instruction MySQL UPDATE 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 devrais-je recevoir cette erreur? J'ai même essayé de redémarrer mon serveur MySQL et cela n'a pas aidé.

La table a 406 733 lignes.

192
Jason Swett

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 détient un verrou d’enregistrement sur un enregistrement (vous mettez à jour tous les enregistrements de la table!) Pendant trop longtemps et votre thread est en cours d’expiration.

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'éditeur sql). Idéalement, faites cela sur une machine d'essai silencieuse.

164
MarkR

COMMENT FORCER UNLOCK pour les tables verrouillées dans MySQL:

Briser les verrous de cette manière peut empêcher atomicity de la base de données de s'exécuter sur les instructions SQL à l'origine du verrou.

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

1) Entrez 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 verrouillant votre (vos) table (s)

mysql> show processlist;

4) Tuez un de ces processus

mysql> kill <put_process_id_here>;
252
Eric Leschinski
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   |
+--------------------------+-------+

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

76
veen

Regardez si votre base de données est bien ajustée. Surtout l'isolement des transactions. N’est pas une bonne idée d’augmenter la variable innodb_lock_wait_timeout.

Vérifiez le niveau d'isolation de votre transaction de base de données dans la cli mysql:

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

Vous pourriez obtenir des améliorations en changeant le niveau d'isolement, utilisez Oracle comme READ COMMITTED à la place 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.

62
saisyukusanagi

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. Il est fort probable qu'une autre requête met à jour, insère ou supprime l'une des tables de votre requête. Vous devez savoir ce que c'est:

SHOW PROCESSLIST;

Une fois que vous avez localisé le processus de blocage, trouvez sa id et exécutez: 

KILL {id};

Relancez votre requête initiale.

12
BassMHL

100% avec ce que MarkR a dit. autocommit transforme chaque déclaration en une transaction.

SHOW ENGINE INNODB STATUS devrait vous donner des indices sur la raison de l'impasse. Examinez également votre journal de requête lent pour voir ce qui reste interroger la table et essayez de supprimer tout ce qui fait une analyse de table complète. Le verrouillage au niveau des lignes fonctionne bien, mais pas lorsque vous essayez de verrouiller toutes les lignes!

12
James C

Pouvez-vous mettre à jour tout autre enregistrement dans cette table ou cette table est-elle fortement utilisée? Ce que je pense, c’est que lorsqu’il tente d’obtenir un verrou, il lui faut mettre à jour cet enregistrement, le délai d’expiration défini a expiré. Vous pourrez peut-être augmenter le temps, ce qui peut aider.

5
John Kane

Le nombre de lignes n'est pas énorme ... Créez un index sur account_import_id s'il ne s'agit pas de la clé primaire.

CREATE INDEX idx_customer_account_import_id ON customer (account_import_id);
1
gladiator

Assurez-vous que les tables de base de données utilisent le moteur de stockage InnoDB et le niveau d'isolation de transaction 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 READ-COMMITTED, vous devez le définir. Avant de le configurer, assurez-vous que vous avez les privilèges SUPER dans mysql.

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

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


Vous voudrez peut-être aussi vérifier que vous n'essayez pas de le mettre à jour simultanément dans deux processus. Les utilisateurs (@tala) ont rencontré des messages d'erreur similaires dans ce contexte, vérifiez peut-être que ...

1
Ravi Chhatrala

Ce genre de chose m’est arrivé lorsque j’utilisais php Language construct exit; en cours de transaction. Alors cette transaction "Se bloque" et vous devez tuer le processus mysql (décrit ci-dessus avec processlist;)

0
TomoMiha

Dans mon cas, j'exécutais une requête anormale pour réparer les données. Si vous verrouillez les tables dans votre requête, vous n'aurez plus à 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: Manuel de référence MySQL 8.0

0
Jeff Luyet

En retard pour la fête (comme d'habitude), toutefois, mon problème était le fait que j'avais écrit du mauvais code SQL (étant un novice) et que plusieurs processus avaient un verrou sur le (s) disque (s). J'ai fini par devoir simplement: SHOW PROCESSLIST et ensuite tuer les identifiants avec KILL <id>

0
Smitty

J'ai rencontré 2 Doctrine DBAL, l'une d'elles non transactionnelle (pour les journaux importants), elles sont conçues pour être exécutées en parallèle, sans dépendre l'une de l'autre.

CodeExecution(
    TransactionConnectionQuery()
    TransactionlessConnectionQuery()
)

Mes tests d'intégration ont été intégrés aux transactions pour la restauration des données après chaque test.

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

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

0
Fabian Picone

Si vous venez de tuer une grosse requête, il faudra du temps à rollback. Si vous émettez une autre requête avant que la requête supprimée ne soit restaurée, vous risquez d'obtenir une erreur de délai de verrouillage. C'est ce qui m'est arrivé. La solution était juste d'attendre un peu.

Détails:

J'avais lancé une requête DELETE pour supprimer environ 900 000 lignes sur un million environ.

J'ai exécuté ceci par erreur (supprime seulement 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 immédiatement exécuté la commande correcte et une erreur lock timeout exceeded est survenue peu après. J'ai réalisé que le verrou pourrait en réalité être la rollback de la requête tuée se produisant toujours en arrière-plan. J'ai donc attendu quelques secondes et j'ai relancé la requête.

0
Buttle Butkus

Je venais de Google et je voulais simplement ajouter la solution qui me convenait le mieux. Mon problème était que j'essayais de supprimer les enregistrements d'une grande table qui contenait beaucoup de FK en cascade, alors j'ai eu la même erreur que l'OP.

J'ai désactivé la autocommit et ensuite cela a fonctionné en ajoutant simplement COMMIT à la fin de la phrase SQL. Autant que je sache, cela libère le tampon bit par bit au lieu d'attendre la fin de la commande.

Pour rester sur 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 la autocommit si vous souhaitez laisser la configuration de MySQL comme auparavant.

mysql> set autocommit=1;

0
Kamae