Das Zeitlimit für Sperren wurde überschritten. Versuchen Sie, die Transaktion neu zu starten “, obwohl ich keine Transaktion verwende

266

Ich führe die folgende MySQL- UPDATEAnweisung aus:

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

Ich verwende keine Transaktion. Warum sollte ich diesen Fehler erhalten? Ich habe sogar versucht, meinen MySQL-Server neu zu starten, aber es hat nicht geholfen.

Die Tabelle enthält 406.733 Zeilen.

Jason Swett
quelle

Antworten:

211

Sie verwenden eine Transaktion. Autocommit deaktiviert keine Transaktionen, sondern bewirkt lediglich, dass sie am Ende der Anweisung automatisch festgeschrieben werden.

Was passiert ist, dass ein anderer Thread zu lange eine Datensatzsperre für einen Datensatz hält (Sie aktualisieren jeden Datensatz in der Tabelle!) Und Ihr Thread eine Zeitüberschreitung aufweist.

Sie können weitere Details des Ereignisses anzeigen, indem Sie a ausgeben

SHOW ENGINE INNODB STATUS

nach der Veranstaltung (im sqlEditor). Idealerweise auf einer leisen Testmaschine.

MarkR
quelle
1
Gibt es eine Möglichkeit, die Ausgabe in einer Datei zu speichern? Ich habe SHOW ENGINE INNODB STATUS \ G> innodb_stat.txt ausprobiert, funktioniert aber nicht.
Yantaq
14
Über die Befehlszeile: mysql [Anmeldeinformationen einfügen] -e "SHOW ENGINE INNODB STATUS \ G"> innodb_stat.txt
VenerableAgents
Wenn viele MySQL-Threads (oder -Prozesse) ausgelastet sind, z. B. einige Abfragen sehr lange benötigen, müssen Sie einige warten, processbis sie inaktiv sind. Wenn ja, könnten Sie diesen Fehler bekommen. Habe ich recht?
Zhuguowei
6
Das Ausführen mehrerer (2+) UPDATE-Abfragen in derselben Zeile während einer einzelnen Transaktion führt ebenfalls zu diesem Fehler.
Okneloper
Für diejenigen, die Python MySQL Connector verwenden connection.commit(), verwenden Sie zum Festschreiben des INSERToder UPDATESie haben gerade durchgeschossen.
VRE
334

So erzwingen Sie das Entsperren für gesperrte Tabellen in MySQL:

Das Aufbrechen solcher Schlösser kann zu Atomizität führen Aufheben solcher in der Datenbank für die SQL-Anweisungen, die die Sperre verursacht haben, nicht erzwungen wird.

Dies ist hackisch und die richtige Lösung besteht darin, Ihre Anwendung zu reparieren, die die Sperren verursacht hat. Wenn jedoch Dollars auf dem Spiel stehen, bringt ein schneller Tritt die Dinge wieder in Bewegung.

1) Geben Sie MySQL ein

mysql -u your_user -p

2) Sehen wir uns die Liste der gesperrten Tabellen an

mysql> show open tables where in_use>0;

3) Sehen wir uns die Liste der aktuellen Prozesse an. Einer von ihnen sperrt Ihre Tabelle (n).

mysql> show processlist;

4) Beenden Sie einen dieser Prozesse

mysql> kill <put_process_id_here>;
Eric Leschinski
quelle
14
Das ist gefährlich und hackisch. Eine geeignete Lösung besteht darin, Ihre Anwendung zu reparieren.
Zenexer
71
Unsinn, damit können Sie ein Durcheinander rückgängig machen und dann die Anwendung reparieren. Wenn ich diesem Kerl 100 Stimmen für dieses Problem geben könnte, das ich JETZT beheben müsste, würde ich es tun.
Lizardx
7
Ich stimme Lizardx zu. Dies war eine sehr nützliche Lösung in der Situation, dass ich nicht das Privileg hatte, SHOW ENGINE INNODB STATUS
Travis Schneeberger
8
Wie ist es gefährlich , eine lang laufende Abfrage auf diese Weise zu beenden ? Der Client, der anruft, erhält nur eine Fehlermeldung.
Xeoncross
7
@EricLeschinski Ich verstehe, aber ich muss fragen, warum in aller Welt Sie eine schlampige Datenbank wie MySQL auf einem lebenskritischen System verwenden würden.
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   |
+--------------------------+-------+

Lösen Sie nun die Sperre erneut aus. Sie haben 100 Sekunden Zeit, um eine SHOW ENGINE INNODB STATUS\Gan die Datenbank zu senden und festzustellen, welche andere Transaktion Ihre sperrt.

veen
quelle
8
Diese Antwort erklärt nicht, warum der Fragesteller seinen Fehler erhält. Könnten Sie näher erläutern, warum Sie nicht nur die Antwort geben?
Schlitten
5
+1 Obwohl dies die Frage nicht direkt beantwortet, ist es für mich eine gute Referenz, um dieses Problem zu umgehen.
Jossef Harush
1
@ArtB dev.mysql.com/doc/innodb/1.1/en/… Im Wesentlichen empfängt das OP den Fehler, weil eine Sperre für die Tabelle aufgerufen wurde und die Zeit bis zum Beenden der Transaktion den lock_wait_timeoutWert überschritten hat
fyrye
70

Überprüfen Sie, ob Ihre Datenbank genau abgestimmt ist. Besonders die Transaktionsisolation. Es ist keine gute Idee, die Variable innodb_lock_wait_timeout zu erhöhen.

Überprüfen Sie die Isolationsstufe Ihrer Datenbanktransaktion in der MySQL-CLI:

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)

Sie könnten Verbesserungen erhalten, wenn Sie die Isolationsstufe ändern. Verwenden Sie das Orakel wie READ COMMITTED anstelle von 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> 

Verwenden Sie SELECT FOR UPDATE auch nur, wenn dies erforderlich ist.

saisyukusanagi
quelle
1
Dies ist eine großartige Lösung für Sperrprobleme.
duftend
3
Funktioniert hervorragend für mich, die my.cnf-Version ist [mysqld] Transaction-Isolation = READ-COMMITTED
Benoit Gauthier
Nur eine Anmerkung: MySQL 8 hat die tx_isolationVariable in umbenannt transaction_isolation.
Xonya
Es muss darauf geachtet werden, zu wissen, worauf Sie sich einlassen, wenn Sie von der Leseisolation zu READ COMMITTED wechseln. Möglicherweise erhalten Sie schmutzige Daten, die Sie vermeiden möchten. Wikipedia Isoloation
huggie
27

Keine der vorgeschlagenen Lösungen hat bei mir funktioniert, aber das hat funktioniert.

Etwas blockiert die Ausführung der Abfrage. Höchstwahrscheinlich wird eine andere Abfrage aktualisiert, eingefügt oder aus einer der Tabellen in Ihrer Abfrage gelöscht. Sie müssen herausfinden, was das ist:

SHOW PROCESSLIST;

Sobald Sie den Blockierungsprozess gefunden haben, suchen Sie ihn idund führen Sie Folgendes aus:

KILL {id};

Führen Sie Ihre erste Abfrage erneut aus.

BassMHL
quelle
Ich habe versehentlich alle mit SHOW PROCESSLIST aufgelisteten Prozesse getötet. Jetzt bekomme ich 500 Fehler in phpmyadmin. Bezieht sich dieser 500-Fehler auf das Beenden dieser Prozesse? Wenn ja, wie kann ich es neu starten?
Dashrath
Ich kann sehen, was Sie beschreiben, aber das Beenden des Prozesses funktioniert nicht. Der Befehl 'process' wird "beendet", bleibt jedoch in der Prozessliste.
Torsten
12

100% mit dem, was MarkR gesagt hat. Autocommit macht jede Anweisung zu einer Transaktion mit einer Anweisung.

SHOW ENGINE INNODB STATUSsollte Ihnen einige Hinweise auf den Grund des Deadlocks geben. Sehen Sie sich auch Ihr langsames Abfrageprotokoll genau an, um zu sehen, was die Tabelle sonst noch abfragt, und versuchen Sie, alles zu entfernen, was einen vollständigen Tabellenscan ausführt. Das Sperren auf Zeilenebene funktioniert gut, aber nicht, wenn Sie versuchen, alle Zeilen zu sperren!

James C.
quelle
5

Können Sie einen anderen Datensatz in dieser Tabelle aktualisieren oder wird diese Tabelle häufig verwendet? Ich denke, während des Versuchs, eine Sperre zu erhalten, die zum Aktualisieren dieses Datensatzes erforderlich ist, ist das festgelegte Zeitlimit abgelaufen. Möglicherweise können Sie die Zeit verlängern, die möglicherweise hilfreich ist.

John Kane
quelle
3
vielleicht innodb_lock_wait_timeout inmy.cnf
oblig
1
Ich habe in my.cnf Folgendes festgelegt: <br/> innodb_lock_wait_timeout = 120 <br/> Der Standardwert ist 50 für MySQL 5.5. Nach dieser Änderung konnte ich dieses Problem in meinen Unit-Tests nicht sehen! Dies geschah nach dem Wechsel von Proxool zu Tomcat JDBC-Pool. Wahrscheinlich wegen längerer Transaktionszeit mit Tomcat Pool?!
Champ
3

Die Anzahl der Zeilen ist nicht sehr groß ... Erstellen Sie einen Index für account_import_id, wenn dies nicht der Primärschlüssel ist.

CREATE INDEX idx_customer_account_import_id ON customer (account_import_id);
Gladiator
quelle
OMG ... das hat mich gerade gerettet. Ich habe eine Produktions-DB königlich verschraubt, indem ich einen Index gelöscht habe, und dies hat es behoben. Danke.
Nick Gotch
2

Wenn Sie gerade eine große Abfrage beendet haben, dauert es einige Zeit rollback . Wenn Sie eine andere Abfrage ausgeben, bevor das Zurücksetzen der getöteten Abfrage abgeschlossen ist, wird möglicherweise ein Fehler beim Sperren-Timeout angezeigt. Das ist mir passiert. Die Lösung bestand darin, ein bisschen zu warten.

Einzelheiten:

Ich hatte eine DELETE-Abfrage ausgegeben, um ungefähr 900.000 aus ungefähr 1 Million Zeilen zu entfernen.

Ich habe dies versehentlich ausgeführt (entfernt nur 10% der Zeilen): DELETE FROM table WHERE MOD(id,10) = 0

Stattdessen (entfernt 90% der Zeilen): DELETE FROM table WHERE MOD(id,10) != 0

Ich wollte 90% der Zeilen entfernen, nicht 10%. Also habe ich den Prozess in der MySQL-Befehlszeile abgebrochen, da ich wusste, dass er alle bisher gelöschten Zeilen zurücksetzen würde.

Dann habe ich sofort den richtigen Befehl ausgeführt und lock timeout exceededkurz darauf einen Fehler erhalten. Mir wurde klar, dass die Sperre tatsächlich rollbackdie getötete Abfrage sein könnte, die immer noch im Hintergrund stattfindet. Also habe ich ein paar Sekunden gewartet und die Abfrage erneut ausgeführt.

Buttle Butkus
quelle
1

Stellen Sie sicher, dass die Datenbanktabellen die InnoDB-Speicher-Engine und die Transaktionsisolationsstufe READ-COMMITTED verwenden.

Sie können dies überprüfen, indem Sie SELECT @@ GLOBAL.tx_isolation, @@ tx_isolation; auf MySQL-Konsole.

Wenn es nicht auf READ-COMMITTED eingestellt ist, müssen Sie es einstellen. Stellen Sie vor dem Einstellen sicher, dass Sie über SUPER-Berechtigungen in MySQL verfügen.

Sie können Hilfe von nehmen http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html erhalten .

Wenn Sie dies einstellen, wird Ihr Problem wahrscheinlich gelöst.


Möglicherweise möchten Sie auch überprüfen, ob Sie nicht versuchen, dies in zwei Prozessen gleichzeitig zu aktualisieren. Benutzer (@tala) haben in diesem Zusammenhang ähnliche Fehlermeldungen festgestellt. Überprüfen Sie möglicherweise, ob ...

Ravi Chhatrala
quelle
1

Ich kam von Google und wollte nur die Lösung hinzufügen, die für mich funktioniert. Mein Problem war, dass ich versuchte, Datensätze einer riesigen Tabelle zu löschen, die viel FK in Kaskade hatte, sodass ich den gleichen Fehler wie beim OP bekam.

Ich habe das deaktiviert autocommitund dann hat es einfach funktioniertCOMMIT am Ende des SQL-Satzes . Soweit ich verstanden habe, gibt dies den Puffer Stück für Stück frei, anstatt am Ende des Befehls zu warten.

Um mit dem Beispiel des OP Schritt zu halten, hätte dies funktionieren müssen:

mysql> set autocommit=0;

mysql> update customer set account_import_id = 1; commit;

Vergessen Sie nicht, das autocommiterneut zu aktivieren , wenn Sie die MySQL-Konfiguration wie zuvor verlassen möchten.

mysql> set autocommit=1;

Kamae
quelle
0

Spät zur Party (wie üblich) war mein Problem jedoch die Tatsache, dass ich schlechtes SQL geschrieben habe (als Anfänger) und mehrere Prozesse eine Sperre für die Datensätze hatten <- nicht sicher, welche Sprache richtig ist. Am Ende musste ich nur: SHOW PROCESSLISTund dann die IDs mit tötenKILL <id>

Smitty
quelle
0

So etwas passierte mir, als ich PHP Language Construct Exit verwendete. mitten in der Transaktion. Dann "hängt" diese Transaktion und Sie müssen den MySQL-Prozess beenden (oben mit Prozessliste beschrieben;)

TomoMiha
quelle
0

In meinem Fall habe ich eine abnormale Abfrage ausgeführt, um Daten zu reparieren. Wenn Sie die Tabellen in Ihrer Abfrage sperren, müssen Sie sich nicht mit dem Sperrzeitlimit befassen:

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

Dies ist wahrscheinlich keine gute Idee für den normalen Gebrauch.

Weitere Informationen finden Sie unter: MySQL 8.0 Referenzhandbuch

Jeff Luyet
quelle
0

Ich bin auf diese mit 2 Doctrine DBAL-Verbindungen gestoßen, von denen eine nicht transaktionsbezogen ist (für wichtige Protokolle). Sie sollen parallel laufen, unabhängig voneinander.

CodeExecution(
    TransactionConnectionQuery()
    TransactionlessConnectionQuery()
)

Meine Integrationstests wurden nach dem Test in Transaktionen für das Daten-Rollback verpackt.

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

Meine Lösung bestand darin, die Wrapping-Transaktion in diesen Tests zu deaktivieren und die Datenbankdaten auf andere Weise zurückzusetzen.

Fabian Picone
quelle
-4

Hatte den gleichen Fehler, obwohl ich nur eine Tabelle mit einem Eintrag aktualisierte, aber nach dem Neustart von MySQL wurde es behoben.

tubostisch
quelle