Dies ergab sich aus dieser verwandten Frage , bei der ich wissen wollte, wie zwei Transaktionen in einem trivialen Fall (bei dem beide nur in einer einzigen Zeile ausgeführt werden) nacheinander ausgeführt werden können. Ich habe eine Antwort erhalten - SELECT ... FOR UPDATE
als erste Zeile beider Transaktionen verwenden -, aber dies führt zu einem Problem: Wenn die erste Transaktion nie festgeschrieben oder zurückgesetzt wird, wird die zweite Transaktion auf unbestimmte Zeit blockiert. Die innodb_lock_wait_timeout
Variable legt die Anzahl der Sekunden fest, nach denen dem Client, der versucht, die zweite Transaktion durchzuführen, die Meldung "Entschuldigung, versuchen Sie es erneut" mitgeteilt wird. Soweit ich das beurteilen kann, wird er es bis zum nächsten Neustart des Servers erneut versuchen. So:
- Sicherlich muss es eine Möglichkeit geben, eine zu erzwingen,
ROLLBACK
wenn eine Transaktion ewig dauert? Muss ich einen Daemon verwenden, um solche Transaktionen zu beenden, und wenn ja, wie würde ein solcher Daemon aussehen? - Wenn eine Verbindung durch
wait_timeout
oderinteractive_timeout
während der Transaktion unterbrochen wird, wird die Transaktion zurückgesetzt? Gibt es eine Möglichkeit, dies von der Konsole aus zu testen?
Erläuterung : innodb_lock_wait_timeout
Legt die Anzahl der Sekunden fest, die eine Transaktion auf die Freigabe einer Sperre wartet, bevor sie aufgibt. Was ich möchte, ist eine Möglichkeit , die Freigabe eines Schlosses zu erzwingen.
Update 1 : Hier ist ein einfaches Beispiel, das zeigt, warum innodb_lock_wait_timeout
nicht ausreicht, um sicherzustellen, dass die zweite Transaktion nicht von der ersten blockiert wird:
START TRANSACTION;
SELECT SLEEP(55);
COMMIT;
Mit der Standardeinstellung von innodb_lock_wait_timeout = 50
wird diese Transaktion nach 55 Sekunden fehlerfrei abgeschlossen. Wenn Sie UPDATE
vor der SLEEP
Zeile eine hinzufügen und dann eine zweite Transaktion von einem anderen Client initiieren, der versucht, SELECT ... FOR UPDATE
dieselbe Zeile zu erstellen, tritt die zweite Transaktion ab, nicht diejenige, die eingeschlafen ist.
Was ich suche, ist eine Möglichkeit, dem erholsamen Schlaf dieser Transaktion ein Ende zu setzen.
Update 2 : Als Reaktion auf die Bedenken von hobodave, wie realistisch das obige Beispiel ist, gibt es hier ein alternatives Szenario: Ein DBA stellt eine Verbindung zu einem Live-Server her und wird ausgeführt
START TRANSACTION
SELECT ... FOR UPDATE
Dabei sperrt die zweite Zeile eine Zeile, in die die Anwendung häufig schreibt. Dann wird der DBA unterbrochen und geht weg, wobei er vergisst, die Transaktion zu beenden. Die Anwendung wird angehalten, bis die Zeile entsperrt ist. Ich möchte die Zeit minimieren, in der die Anwendung aufgrund dieses Fehlers hängen bleibt.
ROLLBACK
die erste Transaktion erzwingen , wenn der Abschluss länger alsn
Sekunden dauert . Gibt es eine Möglichkeit dazu?MYSQL
es keine Konfiguration gibt, um dieses Szenario zu verhindern. Weil es aufgrund der Verantwortungslosigkeit des Clients nicht akzeptabel ist, dass der Server hängen bleibt. Ich fand keine Schwierigkeit, Ihre Frage zu verstehen, auch sie ist so relevant.Antworten:
In mehr als der Hälfte dieses Threads geht es anscheinend darum, wie man Fragen zu ServerFault stellt. Ich denke, die Frage ist sinnvoll und ziemlich einfach: Wie können Sie eine blockierte Transaktion automatisch zurücksetzen?
Eine Lösung, wenn Sie bereit sind, die gesamte Verbindung zu beenden, besteht darin, wait_timeout /active_timeout festzulegen. Siehe /programming/9936699/mysql-rollback-on-transaction-with-lost-disconnected-connection .
quelle
Da Ihre Frage hier in ServerFault gestellt wird, ist es logisch anzunehmen, dass Sie nach einer MySQL-Lösung für ein MySQL-Problem suchen, insbesondere in dem Bereich, in dem ein Systemadministrator und / oder ein DBA über Fachkenntnisse verfügen Abschnitt behandelt Ihre Fragen:
Nein, das wird es nicht. Ich denke du verstehst nicht
innodb_lock_wait_timeout
. Es macht genau das, was Sie brauchen.Es wird mit einem Fehler zurückgegeben, wie im Handbuch angegeben:
innodb_lock_wait_timeout
Sekunden.Standardmäßig wird die Transaktion nicht zurückgesetzt. Es liegt in der Verantwortung Ihres Anwendungscodes, zu entscheiden, wie dieser Fehler behandelt werden soll, ob dies erneut versucht oder ein Rollback durchgeführt wird.
Wenn Sie ein automatisches Rollback wünschen, wird dies auch im Handbuch erläutert:
RE: Ihre zahlreichen Updates und Kommentare
Zunächst haben Sie in Ihren Kommentaren angegeben, dass Sie "beabsichtigt" haben, zu sagen, dass Sie eine Möglichkeit zum Timeout der ersten Transaktion wünschen, die auf unbestimmte Zeit blockiert wird. Dies geht aus Ihrer ursprünglichen Frage nicht hervor und steht im Widerspruch zu "Wenn die erste Transaktion niemals festgeschrieben oder zurückgesetzt wird, wird die zweite Transaktion auf unbestimmte Zeit blockiert".
Trotzdem kann ich diese Frage auch beantworten. Das MySQL-Protokoll hat kein "Abfrage-Timeout". Dies bedeutet, dass Sie das Zeitlimit für die erste blockierte Transaktion nicht überschreiten können. Sie müssen warten, bis es beendet ist, oder die Sitzung beenden. Wenn die Sitzung beendet wird, setzt der Server die Transaktion automatisch zurück.
Die einzige andere Alternative wäre, eine MySQL-Bibliothek zu verwenden oder zu schreiben, die nicht blockierende E / A verwendet, wodurch Ihre Anwendung den Thread / Fork, der die Abfrage durchführt, nach N Sekunden beenden kann. Die Implementierung und Verwendung einer solchen Bibliothek geht über den Rahmen von ServerFault hinaus. Dies ist eine geeignete Frage für StackOverflow .
Zweitens haben Sie in Ihren Kommentaren Folgendes angegeben:
Dies war in Ihrer ursprünglichen Frage überhaupt nicht ersichtlich und ist es immer noch nicht. Dies konnte erst erkannt werden, nachdem Sie diesen ziemlich wichtigen Leckerbissen im Kommentar geteilt hatten.
Wenn dies tatsächlich das Problem ist, das Sie lösen möchten, haben Sie es leider im falschen Forum gefragt. Sie haben ein Programmierproblem auf Anwendungsebene beschrieben, für das eine Programmierlösung erforderlich ist, die MySQL nicht bereitstellen kann und die außerhalb des Bereichs dieser Community liegt. Ihre letzte Antwort beantwortet die Frage "Wie verhindere ich, dass ein Ruby-Programm eine Endlosschleife durchläuft?". Diese Frage ist für diese Community nicht thematisch und sollte auf StackOverflow gestellt werden .
quelle
innodb_lock_wait_timeout
vorschlagen), ist dies in der von mir gestellten Situation nicht der Fall: Wo der Client hängt oder abstürzt, bevor er die Änderung erhält, einCOMMIT
oder auszuführenROLLBACK
.COMMIT
oder eineROLLBACK
Nachricht gesendet wird, um die erste Transaktion aufzulösen? Hobodave, vielleicht wäre es hilfreich, wenn Sie Ihren Testcode vorlegen würden.In einer ähnlichen Situation entschied sich mein Team für pt-kill ( https://www.percona.com/doc/percona-toolkit/2.1/pt-kill.html ). Es kann Abfragen melden oder beenden, die (unter anderem) zu lange ausgeführt werden. Es ist dann möglich, es alle X Minuten in einem Cron auszuführen.
Das Problem bestand darin, dass eine Abfrage mit einer unerwartet langen (z. B. unbestimmten) Ausführungszeit eine Sicherungsprozedur sperrte, die versuchte, Tabellen mit Lesesperre zu leeren, wodurch wiederum alle anderen Abfragen zum "Warten auf das Löschen von Tabellen" gesperrt wurden, die dann nie abgelaufen sind weil sie nicht auf eine Sperre warten, was zu Fehlern in der Anwendung führte, was letztendlich dazu führte, dass keine Warnungen an Administratoren und Anwendungen angehalten wurden.
Das Update bestand offensichtlich darin, die anfängliche Abfrage zu korrigieren. Um jedoch zu vermeiden, dass in Zukunft versehentlich etwas passiert, wäre es großartig, wenn Transaktionen / Abfragen, die länger als eine bestimmte Zeit dauern, automatisch beendet (oder gemeldet) werden könnten. Welcher Autor der Frage scheint zu fragen. Dies ist mit pt-kill möglich.
quelle
Eine einfache Lösung besteht darin, eine gespeicherte Prozedur zu haben, um die Abfragen zu beenden, die länger dauern als die erforderliche
timeout
Zeit, und dieinnodb_rollback_on_timeout
Option zusammen mit dieser zu verwenden.quelle
Nach einer Weile googeln sieht es so aus, als gäbe es keinen direkten Möglichkeit zu geben, ein Zeitlimit pro Transaktion . Hier ist die beste Lösung, die ich finden konnte:
Der Befehl
gibt Ihnen eine Tabelle mit allen aktuell ausgeführten Befehlen und der Zeit (in Sekunden) seit ihrem Start. Wenn ein Client keine Befehle mehr erteilt, wird er als
Sleep
Befehl bezeichnet, wobei dieTime
Spalte die Anzahl der Sekunden seit dem Abschluss seines letzten Befehls angibt. Sie können also manuell darauf zugreifen undKILL
alles, was einenTime
Wert von mehr als 5 Sekunden hat, wenn Sie sicher sind, dass keine Abfrage länger als 5 Sekunden in Ihrer Datenbank dauern sollte. Wenn der Prozess mit der ID 3 beispielsweise den Wert 12 in seinerTime
Spalte hat, können Sie dies tunDie Dokumentation für die KILL-Syntax schlägt vor, dass eine Transaktion, die vom Thread mit dieser ID ausgeführt wird, zurückgesetzt wird (obwohl ich dies nicht getestet habe, seien Sie also vorsichtig).
Aber wie kann man dies automatisieren und alle 5 Sekunden alle Überstundenskripte beenden? Der erste Kommentar auf derselben Seite gibt uns einen Hinweis, aber der Code ist in PHP; Es scheint, dass wir im MySQL-Client kein
SELECT
On durchführen könnenSHOW PROCESSLIST
. Angenommen, wir haben einen PHP-Daemon, den wir alle$MAX_TIME
Sekunden ausführen , könnte er ungefähr so aussehen:Beachten Sie, dass dies den Nebeneffekt hat, dass alle inaktiven Clientverbindungen gezwungen werden, die Verbindung wiederherzustellen, nicht nur diejenigen, die sich schlecht verhalten.
Wenn jemand eine bessere Antwort hat, würde ich sie gerne hören.
Bearbeiten: Es besteht mindestens ein ernstes Risiko,
KILL
auf diese Weise zu verwenden. Angenommen, Sie verwenden das obige Skript fürKILL
jede Verbindung, die 10 Sekunden lang inaktiv ist. Nachdem eine Verbindung 10 Sekunden lang inaktiv war, aber bevor dieKILL
ausgegeben wird, gibt der Benutzer, dessen Verbindung unterbrochen wird, einenSTART TRANSACTION
Befehl aus. Sie sind dannKILL
ed. Sie senden eineUPDATE
und geben dann, zweimal überlegen, eine ausROLLBACK
. Da dieKILL
ursprüngliche Transaktion jedoch zurückgesetzt wurde, wurde das Update sofort durchgeführt und kann nicht zurückgesetzt werden! In diesem Beitrag finden Sie einen Testfall.quelle
Wie Hobodave in einem Kommentar vorgeschlagen hat, ist es bei einigen Clients (obwohl anscheinend nicht das
mysql
Befehlszeilenprogramm) möglich, ein Zeitlimit für eine Transaktion festzulegen. Hier ist eine Demonstration mit ActiveRecord in Ruby:In diesem Beispiel läuft die Transaktion nach 5 Sekunden ab und wird automatisch zurückgesetzt . ( Aktualisierung als Antwort auf den Kommentar von hobodave: Wenn die Datenbank länger als 5 Sekunden benötigt, um zu antworten, wird die Transaktion sofort zurückgesetzt - nicht früher.) Wenn Sie sicherstellen möchten , dass alle Ihre Transaktionen nach Ablauf der Zeit ablaufen
n
Sekunden ablaufen, Sie können einen Wrapper um ActiveRecord erstellen. Ich vermute, dass dies auch für die beliebtesten Bibliotheken in Java, .NET, Python usw. gilt, aber ich habe sie noch nicht getestet. (Wenn ja, schreiben Sie bitte einen Kommentar zu dieser Antwort.)Transaktionen in ActiveRecord haben im
KILL
Gegensatz zu Transaktionen über die Befehlszeile auch den Vorteil, dass sie sicher sind, wenn a ausgegeben wird. Siehe /dba/1561/mysql-client-believes-theyre-in-a-transaction-gets-killed-wreaks-havoc .Es scheint nicht möglich zu sein, eine maximale Transaktionszeit auf der Serverseite durchzusetzen, außer durch ein Skript wie das, das ich in meiner anderen Antwort gepostet habe.
quelle
Foo.create
Befehl 5 Minuten lang blockiert würde, würde das Timeout ihn nicht beenden. Dies ist nicht kompatibel mit dem in Ihrer Frage angegebenen Beispiel. ASELECT ... FOR UPDATE
könnte leicht für lange Zeiträume blockieren, ebenso wie jede andere Abfrage.ActiveRecord::Base#find_by_sql
: gist.github.com/856100 Dies liegt wiederum daran, dass AR blockierende E / A verwendet.timeout
Methode setzt die Transaktion zurück, jedoch erst, wenn die MySQL-Datenbank auf eine Abfrage antwortet. Dietimeout
Methode eignet sich daher zum Verhindern langer Transaktionen auf der Clientseite oder langer Transaktionen, die aus mehreren relativ kurzen Abfragen bestehen, jedoch nicht für lange Transaktionen, bei denen eine einzelne Abfrage der Schuldige ist.