Ich habe eine innoDB-Tabelle, die Online-Benutzer aufzeichnet. Es wird bei jeder Seitenaktualisierung durch einen Benutzer aktualisiert, um zu verfolgen, auf welchen Seiten er sich befindet und wann er zuletzt auf die Site zugegriffen hat. Ich habe dann einen Cron, der alle 15 Minuten läuft, um alte Datensätze zu löschen.
Ich habe einen Deadlock gefunden, als ich versucht habe, einen Lock zu bekommen. Versuchen Sie, die Transaktion 'letzte Nacht etwa 5 Minuten lang neu zu starten. Dies scheint der Fall zu sein, wenn INSERTs in dieser Tabelle ausgeführt werden. Kann jemand vorschlagen, wie dieser Fehler vermieden werden kann?
=== BEARBEITEN ===
Hier sind die Abfragen, die ausgeführt werden:
Erster Besuch auf der Website:
INSERT INTO onlineusers SET
ip = 123.456.789.123,
datetime = now(),
userid = 321,
page = '/thispage',
area = 'thisarea',
type = 3
Auf jeder Seite aktualisieren:
UPDATE onlineusers SET
ips = 123.456.789.123,
datetime = now(),
userid = 321,
page = '/thispage',
area = 'thisarea',
type = 3
WHERE id = 888
Cron alle 15 Minuten:
DELETE FROM onlineusers WHERE datetime <= now() - INTERVAL 900 SECOND
Es zählt dann einige Statistiken, um einige Statistiken zu protokollieren (z. B. Mitglieder online, Besucher online).
Antworten:
Ein einfacher Trick, der bei den meisten Deadlocks helfen kann, besteht darin, die Vorgänge in einer bestimmten Reihenfolge zu sortieren.
Sie erhalten einen Deadlock, wenn zwei Transaktionen versuchen, zwei Sperren in entgegengesetzter Reihenfolge zu sperren, dh:
Wenn beide gleichzeitig ausgeführt werden, sperrt Verbindung 1 den Schlüssel (1), Verbindung 2 den Schlüssel (2) und jede Verbindung wartet darauf, dass die andere den Schlüssel freigibt -> Deadlock.
Wenn Sie nun Ihre Abfragen so geändert haben, dass die Verbindungen die Schlüssel in derselben Reihenfolge sperren, dh:
Es wird unmöglich sein, einen Deadlock zu bekommen.
Das schlage ich also vor:
Stellen Sie sicher, dass Sie außer der delete-Anweisung keine anderen Abfragen haben, die den Zugriff auf mehr als einen Schlüssel gleichzeitig sperren. Wenn Sie dies tun (und ich vermute, dass Sie dies tun), ordnen Sie deren WO in (k1, k2, .. kn) in aufsteigender Reihenfolge an.
Korrigieren Sie Ihre Löschanweisung so, dass sie in aufsteigender Reihenfolge funktioniert:
Veränderung
Zu
Eine andere Sache, die Sie beachten sollten, ist, dass die MySQL-Dokumentation vorschlägt, dass der Client im Falle eines Deadlocks automatisch erneut versuchen sollte. Sie können diese Logik zu Ihrem Client-Code hinzufügen. (Sagen wir, 3 Versuche zu diesem bestimmten Fehler, bevor Sie aufgeben).
quelle
Ein Deadlock tritt auf, wenn zwei Transaktionen aufeinander warten, um eine Sperre zu erhalten. Beispiel:
Es gibt zahlreiche Fragen und Antworten zu Deadlocks. Jedes Mal, wenn Sie eine Zeile einfügen / aktualisieren / oder löschen, wird eine Sperre erworben. Um einen Deadlock zu vermeiden, müssen Sie sicherstellen, dass gleichzeitige Transaktionen die Zeile nicht in einer Reihenfolge aktualisieren, die zu einem Deadlock führen kann. Versuchen Sie im Allgemeinen, die Sperre auch in verschiedenen Transaktionen immer in derselben Reihenfolge zu erhalten (z. B. immer zuerst Tabelle A, dann Tabelle B).
Ein weiterer Grund für einen Deadlock in der Datenbank können fehlende Indizes sein . Wenn eine Zeile eingefügt / aktualisiert / gelöscht wird, muss die Datenbank die relationalen Einschränkungen überprüfen, dh sicherstellen, dass die Beziehungen konsistent sind. Dazu muss die Datenbank die Fremdschlüssel in den zugehörigen Tabellen überprüfen. Es könnte in anderer Schloss ist als die Zeile erworben führen , die geändert wird. Stellen Sie dann sicher, dass die Fremdschlüssel (und natürlich die Primärschlüssel) immer indexiert sind, da dies sonst zu einer Tabellensperre anstelle einer Zeilensperre führen kann . Wenn eine Tabellensperre auftritt, ist der Sperrenkonflikt höher und die Wahrscheinlichkeit eines Deadlocks steigt.
quelle
Es ist wahrscheinlich, dass die delete-Anweisung einen großen Teil der gesamten Zeilen in der Tabelle beeinflusst. Dies kann möglicherweise dazu führen, dass beim Löschen eine Tabellensperre aktiviert wird. Das Festhalten an einer Sperre (in diesem Fall Zeilen- oder Seitensperren) und der Erwerb weiterer Sperren ist immer ein Deadlock-Risiko. Ich kann jedoch nicht erklären, warum die insert-Anweisung zu einer Eskalation der Sperren führt - dies hat möglicherweise mit dem Teilen / Hinzufügen von Seiten zu tun, aber jemand, der MySQL besser kennt, muss sie dort ausfüllen.
Zunächst kann es sich lohnen, sofort explizit eine Tabellensperre für die delete-Anweisung zu erwerben. Siehe Probleme mit LOCK TABLES und Table Locking .
quelle
Sie können versuchen, diesen
delete
Job auszuführen, indem Sie zuerst den Schlüssel jeder zu löschenden Zeile in eine temporäre Tabelle wie diesen Pseudocode einfügenEine solche Aufteilung ist weniger effizient, vermeidet jedoch die Notwendigkeit, während des Vorgangs ein Schlüsselbereichsschloss zu halten
delete
.Ändern Sie außerdem Ihre
select
Abfragen, um einewhere
Klausel hinzuzufügen , die Zeilen ausschließt, die älter als 900 Sekunden sind. Dies vermeidet die Abhängigkeit vom Cron-Job und ermöglicht es Ihnen, ihn neu zu planen, damit er seltener ausgeführt wird.Theorie über die Deadlocks: Ich habe nicht viel Hintergrundwissen in MySQL, aber hier
delete
ist es ... Das wird eine Schlüsselbereichssperre für datetime halten, um zu verhindern, dass Zeilen, die mit seinerwhere
Klausel übereinstimmen, während der Transaktion hinzugefügt werden Wenn Zeilen zum Löschen gefunden werden, wird versucht, auf jeder Seite, die geändert wird, eine Sperre zu erhalten. Derinsert
wird eine Sperre für die Seite erhalten, in die er eingefügt wird, und dann versuchen, die Schlüsselsperre zu erhalten. Normalerweiseinsert
wartet der geduldig darauf, dass sich diese Tastensperre öffnet, aber dies blockiert, wenndelete
versucht wird, dieselbe Seite zu sperren, die erinsert
verwendet, weil erdelete
diese Seitensperre und dieinsert
Tastensperre benötigt. Dies scheint jedoch nicht richtig für Einfügungen zu sein, diedelete
undinsert
verwenden Datums- / Uhrzeitbereiche, die sich nicht überlappen, sodass möglicherweise etwas anderes passiert.http://dev.mysql.com/doc/refman/5.1/en/innodb-next-key-locking.html
quelle
Falls noch jemand mit diesem Problem zu kämpfen hat:
Ich hatte ein ähnliches Problem, bei dem zwei Anfragen gleichzeitig auf den Server trafen. Es gab keine Situation wie unten:
Also war ich verwirrt, warum ein Deadlock passiert.
Dann stellte ich fest, dass zwischen zwei Tabellen aufgrund eines Fremdschlüssels eine Eltern-Kind-Beziehung bestand. Als ich einen Datensatz in eine untergeordnete Tabelle einfügte, erwarb die Transaktion eine Sperre für die Zeile der übergeordneten Tabelle. Unmittelbar danach habe ich versucht, die übergeordnete Zeile zu aktualisieren, die die Erhöhung der Sperre auf EXKLUSIV auslöste. Da die zweite gleichzeitige Transaktion bereits eine SHARED-Sperre enthielt, verursachte sie einen Deadlock.
Siehe: https://blog.tekenlight.com/2019/02/21/database-deadlock-mysql.html
quelle
Für Java-Programmierer, die Spring verwenden, habe ich dieses Problem vermieden, indem ich einen AOP-Aspekt verwendet habe, der automatisch Transaktionen wiederholt, die auf vorübergehende Deadlocks stoßen.
Weitere Informationen finden Sie unter @RetryTransaction Javadoc.
quelle
Ich habe eine Methode, deren Interna in eine MySqlTransaction eingeschlossen sind.
Das Deadlock-Problem trat für mich auf, als ich dieselbe Methode parallel zu sich selbst ausführte.
Es gab kein Problem beim Ausführen einer einzelnen Instanz der Methode.
Als ich MySqlTransaction entfernte, konnte ich die Methode ohne Probleme parallel zu sich selbst ausführen.
Ich teile nur meine Erfahrungen und befürworte nichts.
quelle
cron
ist gefährlich. Wenn eine Instanz von Cron nicht fertig ist, bevor die nächste fällig ist, kämpfen sie wahrscheinlich gegeneinander.Es wäre besser, einen kontinuierlich laufenden Job zu haben, der einige Zeilen löscht, einige in den Ruhezustand versetzt und dann wiederholt.
Auch
INDEX(datetime)
ist sehr wichtig, um Deadlocks zu vermeiden.Wenn der Datetime-Test jedoch mehr als beispielsweise 20% der Tabelle enthält,
DELETE
führt er einen Tabellenscan durch. Kleinere Blöcke, die häufiger gelöscht werden, sind eine Problemumgehung.Ein weiterer Grund für kleinere Blöcke besteht darin, weniger Zeilen zu sperren.
Endeffekt:
INDEX(datetime)
Andere Löschtechniken: http://mysql.rjweb.org/doc.php/deletebig
quelle