Fixing “Wartezeit für Sperren überschritten; Versuchen Sie, die Transaktion neu zu starten “für eine feststeckende MySQL-Tabelle?

131

Von einem Skript aus habe ich tausende Male eine solche Anfrage an meine lokale Datenbank gesendet:

update some_table set some_column = some_value

Ich habe vergessen, den where-Teil hinzuzufügen, daher wurde dieselbe Spalte für alle Zeilen in der Tabelle auf den gleichen Wert gesetzt. Dies wurde tausende Male durchgeführt und die Spalte wurde indiziert, sodass der entsprechende Index wahrscheinlich zu oft aktualisiert wurde .

Ich habe festgestellt, dass etwas nicht stimmt, weil es zu lange gedauert hat, also habe ich das Drehbuch getötet. Ich habe seitdem sogar meinen Computer neu gestartet, aber etwas ist in der Tabelle hängen geblieben, da die Ausführung einfacher Abfragen sehr lange dauert und wenn ich versuche, den entsprechenden Index zu löschen, schlägt dies mit der folgenden Meldung fehl:

Lock wait timeout exceeded; try restarting transaction

Es ist eine innodb-Tabelle, daher ist die Transaktion wahrscheinlich implizit. Wie kann ich diese Tabelle reparieren und die feststeckende Transaktion daraus entfernen?

Tom
quelle
3
Was ist die Ausgabe von SHOW FULL PROCESSLIST?
Wolph
Es wird nur der Befehl SHOW FULL PROCESSLIST angezeigt, sonst nichts. Es ist eine lokale Entwicklungsdatenbank. Darauf läuft nichts. Ich habe die Fehlermeldung 'lock wait ..' in der Befehlszeile erhalten, als ich versucht habe, den Index von dort zu löschen.
Tom
In diesem Fall erstellen Sie wahrscheinlich zwei separate Verbindungen in verschiedenen Transaktionen, die aufeinander warten müssen.
Wolph
Ich habe danach keine Transaktionen mehr erstellt. Ich habe das Skript beendet, den Computer neu gestartet und mich über die Befehlszeile angemeldet, um mich umzusehen. Die Datenbank wurde nur vom MySQL-Befehlszeilenclient verwendet. Es muss also etwas in der Tabelle stecken geblieben sein.
Tom

Antworten:

143

Ich hatte ein ähnliches Problem und löste es, indem ich die laufenden Threads überprüfte. Verwenden Sie den folgenden Befehl in der MySQL-Befehlszeilenschnittstelle, um die laufenden Threads anzuzeigen:

SHOW PROCESSLIST;

Es kann auch von phpMyAdmin gesendet werden, wenn Sie keinen Zugriff auf die MySQL-Befehlszeilenschnittstelle haben.
Dadurch wird eine Liste von Threads mit entsprechenden IDs und Ausführungszeit angezeigt, sodass Sie die Threads TÖTEN können, deren Ausführung zu lange dauert. In phpMyAdmin haben Sie eine Schaltfläche zum Stoppen von Threads mithilfe von KILL. Wenn Sie die Befehlszeilenschnittstelle verwenden, verwenden Sie einfach den Befehl KILL gefolgt von der Thread-ID, wie im folgenden Beispiel:

KILL 115;

Dadurch wird die Verbindung für den entsprechenden Thread beendet.

Mihai Crăiță
quelle
36
BEACHTEN! Dies wurde von jemandem in einem der vielen SO-Threads zu diesem Problem erwähnt: Manchmal wird der Prozess, der die Tabelle gesperrt hat, in der Prozessliste als schlafend angezeigt! Ich riss mir die Haare aus, bis ich alle Fäden getötet hatte, die in der fraglichen Datenbank offen waren, ob ich schlief oder nicht. Damit wurde die Tabelle endlich entsperrt und die Update-Abfrage ausgeführt. Der Kommentator erwähnte etwas wie "Manchmal sperrt ein MySQL-Thread eine Tabelle und schläft dann, während er darauf wartet, dass etwas passiert, das nicht mit MySQL zu tun hat."
Eric L.
(Ich habe meine eigene Antwort hinzugefügt, um dieses Gedankenfragment hier auf eine verwandte Frage
Eric L.
Das hat mir geholfen. Ich hatte ein paar schlafende Threads, die von der PHPStorm-Datenbankverwaltung / Abfragefunktion erstellt wurden.
Ejaz
Toll! Ich hatte seit 5 Stunden einen versteckten Prozess, den ich identifizieren und töten konnte.
Aldo Paradiso
2
Dies ist keine Lösung mehr als das Abkleben einer infizierten Wunde. Sie sprechen das zugrunde liegende Grundproblem nicht an.
user2914191
55

Sie können die aktuell ausgeführten Transaktionen mit überprüfen

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

Ihre Transaktion sollte eine der ersten sein, da sie die älteste in der Liste ist. Nehmen Sie nun einfach den Wert von trx_mysql_thread_idund senden Sie ihm den KILLBefehl:

KILL 1234;

Wenn Sie sich nicht sicher sind, welche Transaktion Ihnen gehört, wiederholen Sie die erste Abfrage sehr oft und prüfen Sie, welche Transaktionen weiterhin bestehen.

nlsrchtr
quelle
Möglicherweise müssen Sie ein Root-Konto verwenden, um diese SQL auszuführen, um
festzustellen,
40

Überprüfen Sie den InnoDB-Status auf Sperren

SHOW ENGINE InnoDB STATUS;

Überprüfen Sie die offenen MySQL-Tabellen

SHOW OPEN TABLES WHERE In_use > 0;

Überprüfen Sie ausstehende InnoDB-Transaktionen

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

Überprüfen Sie die Sperrabhängigkeit - was blockiert was?

SELECT * FROM `information_schema`.`innodb_locks`;

Nachdem Sie die obigen Ergebnisse untersucht haben, sollten Sie sehen können, was was sperrt.

Die Hauptursache des Problems könnte auch in Ihrem Code liegen. Überprüfen Sie die zugehörigen Funktionen, insbesondere auf Anmerkungen, wenn Sie JPA wie Hibernate verwenden.

Wie hier beschrieben , kann der Missbrauch der folgenden Anmerkung beispielsweise zu Sperren in der Datenbank führen:

@Transactional(propagation = Propagation.REQUIRES_NEW) 
martoncsukas
quelle
4
Ich danke dir sehr! Das Laufen SELECT * FROM information_schema.innodb_trx t JOIN information_schema.processlist p ON t.trx_mysql_thread_id = p.identhüllte den Schuldigen: Der Sperr-Thread kam von meiner On-IP-Adresse ... Ich hatte vergessen, eine Debug-Konsole zu schließen, die ich mitten in einer Transaktion verlassen hatte ...
Elias Strehle
40

Dies passierte mir, als meine Datenbankgröße zunahm und ich viele Transaktionen damit durchführte.

Die Wahrheit ist, dass es wahrscheinlich eine Möglichkeit gibt, entweder Ihre Abfragen oder Ihre Datenbank zu optimieren, aber versuchen Sie diese beiden Abfragen, um das Problem zu umgehen.

Führen Sie Folgendes aus:

SET GLOBAL innodb_lock_wait_timeout = 5000; 

Und dann das:

SET innodb_lock_wait_timeout = 5000; 
Max D.
quelle
2
Referenzlink: innodb_lock_wait_timeout
culix
1
Ich verwende eine sehr ausgelastete 11-GB-Datenbank. Dies ist das einzige, was für mich funktioniert hat, danke!
Dongemus
Denken Sie daran, die Werte auf die vorherigen Werte zu ändern, wenn Sie sie nicht für immer dort behalten möchten.
Ricardo Martins
Dies bedeutet, dass einige meiner Benutzer eine langsamere Erfahrung haben, oder?
Arnold Roa
9

Wenn Sie eine Verbindung für eine Transaktion herstellen, erwerben Sie eine Sperre, bevor Sie die Transaktion ausführen. Wenn Sie das Schloss nicht erwerben können, versuchen Sie es für einige Zeit. Wenn die Sperre immer noch nicht verfügbar ist, wird der Fehler "Wartezeit der Sperre überschritten" ausgelöst. Sie können keine Sperre erwerben, weil Sie die Verbindung nicht schließen. Wenn Sie also zum zweiten Mal versuchen, eine Sperre zu erhalten, können Sie die Sperre nicht erwerben, da Ihre vorherige Verbindung immer noch nicht geschlossen ist und die Sperre hält.

Lösung: Schließen Sie die Verbindung oder setAutoCommit(true)(gemäß Ihrem Design), um die Sperre aufzuheben.

user3388324
quelle
7

Starten Sie MySQL neu, es funktioniert einwandfrei.

ABER Vorsicht, wenn eine solche Abfrage nicht funktioniert, liegt irgendwo ein Problem vor:

  • in Ihrer Anfrage (falsch platziertes Zeichen, kartesisches Produkt, ...)
  • sehr viele Datensätze zu bearbeiten
  • komplexe Verknüpfungen oder Tests (MD5, Teilzeichenfolgen LIKE %...%usw.)
  • Datenstrukturproblem
  • Fremdschlüsselmodell (Ketten- / Schleifenverriegelung)
  • falsch indizierte Daten

Wie @syedrakib sagte, funktioniert es, aber dies ist keine langlebige Lösung für die Produktion.

Achtung: Der Neustart kann sich auf Ihre Daten mit inkonsistentem Status auswirken.

Sie können auch überprüfen, wie MySQL Ihre Abfrage mit dem Schlüsselwort EXPLAIN behandelt, und prüfen, ob dort etwas möglich ist, um die Abfrage zu beschleunigen (Indizes, komplexe Tests, ...).

Benj
quelle
DANKE. Sie haben mein Problem nicht direkt gelöst, aber ich litt unter Tischschlössern und es war so schlimm. Dies ist der einzige Beitrag im gesamten Internet, der mich auf die Idee bringt, die Fremdschlüssel zu überprüfen. Also fand ich heraus, dass der Schlüssel des Primärschlüssels 11 und der Fremdschlüssel 10 war. Ich weiß nicht, wie das passieren könnte und warum alles vorher funktioniert hat.
EscapeNetscape
@EscapeNetscape du bist willkommen, ich bin froh, dass diese kleine Antwort dir geholfen hat :)
Benj
3

Gehe zu Prozessen in MySQL.

Ich kann also sehen, dass die Aufgabe noch funktioniert.

Beenden Sie den jeweiligen Prozess oder warten Sie, bis der Prozess abgeschlossen ist.

Shemeer M Ali
quelle
7
Es löst das Problem. Dies kann jedoch keine Lösung für einen LIVE-Produktionsserver sein. Wie können wir diese Deadlock-Behandlung ausgeben? Oder wie können wir diesen Stillstand vermeiden?
Rakib
1
Nun, es kommt vor, dass selbst bei PERFEKT wohlgeformten und PERFEKT entkommenen MySQL-Abfragen, die nicht einmal vom Entwickler, sondern von der Active-Records-Schnittstelle des Frameworks geschrieben wurden, immer noch Probleme mit dem Wartezeitlimit für Sperren auftreten können. Daher schreibe ich hier keine richtige MySQL-Abfrage.
Rakib
1

Ich bin auf dasselbe Problem mit einer "Update" -Statement gestoßen. Meine Lösung bestand einfach darin, die in phpMyAdmin für die Tabelle verfügbaren Vorgänge durchzugehen. Ich habe den Tisch optimiert, gespült und defragmentiert (nicht in dieser Reihenfolge). Für mich ist es nicht erforderlich, die Tabelle zu löschen und aus dem Backup wiederherzustellen. :) :)

Der Wissenschaftsjunge
quelle
1
Es könnte das Problem lösen. Dies jedoch jedes Mal tun zu müssen, wenn ein solcher Fehler auftritt, kann keine Lösung für einen LIVE-Produktionsserver sein. Wie können wir diese Deadlock-Behandlung ausgeben? Oder wie können wir diesen Stillstand vermeiden?
Rakib
1

Ich hatte das gleiche Problem. Ich denke, es war ein Deadlock-Problem mit SQL. Sie können das Schließen des SQL-Prozesses einfach über den Task-Manager erzwingen. Wenn das nicht behoben wurde, starten Sie einfach Ihren Computer neu. Sie müssen die Tabelle nicht löschen und die Daten neu laden.

Kriver
quelle
Es löst das Problem. Dies kann jedoch keine Lösung für einen LIVE-Produktionsserver sein. Wie können wir diese Deadlock-Behandlung ausgeben? Oder wie können wir diesen Stillstand vermeiden?
Rakib
Starten Sie Apache und seine Dienste (oder zumindest MySQL) neu, kein Neustart
erforderlich
1

Ich hatte dieses Problem beim Versuch, eine bestimmte Gruppe von Datensätzen zu löschen (unter Verwendung von MS Access 2007 mit einer ODBC-Verbindung zu MySQL auf einem Webserver). Normalerweise lösche ich bestimmte Datensätze aus MySQL und ersetze sie dann durch aktualisierte Datensätze (Kaskade lösche mehrere verwandte Datensätze, wodurch das Löschen aller verwandten Datensätze für eine einzelne Datensatzlöschung optimiert wird).

Ich habe versucht, die in phpMyAdmin für die Tabelle verfügbaren Vorgänge (Optimieren, Leeren usw.) durchzugehen, habe jedoch beim Löschen eine erforderliche Berechtigung zum erneuten Laden des Fehlers erhalten. Da sich meine Datenbank auf einem Webserver befindet, konnte ich die Datenbank nicht neu starten. Das Wiederherstellen aus einer Sicherung war keine Option.

Ich habe versucht, eine Löschabfrage für diese Gruppe von Datensätzen auf dem cPanel mySQL-Zugriff im Web auszuführen. Ich habe die gleiche Fehlermeldung erhalten.

Meine Lösung: Ich habe den kostenlosen MySQL-Abfragebrowser von Sun (Oracle) verwendet (den ich zuvor auf meinem Computer installiert habe) und dort die Löschabfrage ausgeführt. Es hat sofort funktioniert, Problem gelöst. Ich konnte dann die Funktion erneut mithilfe des Access-Skripts über die ODBC Access to MySQL-Verbindung ausführen.

Mike Lane
quelle
-2

Behoben.

Stellen Sie sicher, dass in der Abfrage kein nicht übereinstimmender Datentyp eingefügt wurde. Ich hatte ein Problem, bei dem ich "Benutzerbrowser-Agentendaten" ausprobierte VARCHAR(255)und ein Problem mit dieser Sperre hatte, das jedoch TEXT(255)behoben wurde, als ich sie änderte .

Es handelt sich also höchstwahrscheinlich um eine Nichtübereinstimmung des Datentyps.

Hiren Raj
quelle
-150

Ich habe das Problem gelöst, indem ich die Tabelle gelöscht und aus dem Backup wiederhergestellt habe.

Tom
quelle
20
Übrigens gewinnt diese Antwort die Ehre, die niedrigste "Akzeptierte" Antwort von SO zu sein! 🏆
Ashleedawg
1
Dies ist auch die Antwort mit der niedrigsten Punktzahl im Allgemeinen
Bob Kerman