Ich habe ein Problem, bei dem mehrere MySQL-Updates gleichzeitig ausgeführt werden und einige Minuten dauern. Ich verwende InnoDB, daher bin ich verwirrt, warum dies passieren könnte, da bei jedem Update nur eine Zeile aktualisiert wird. Ich verwende auch eine m2.4xlarge RDS-Instanz (die größte, die sie kommen).
Folgendes mache ich: Ich habe eine Tabelle mit ungefähr 100 Millionen Zeilen, wobei "Ansichten" eine Spalte (die indiziert ist) ist, und ich möchte die Ansichten für ungefähr 1 Million Zeilen aktualisieren. Auf mehreren verschiedenen Servern habe ich eine Schleife wie diese, in der jeder Server seine eigenen zu aktualisierenden Zeilen hat (Pseudocode):
mysql("set autocommit=0");
mysql("start transaction");
foreach($rows as $row) {
mysql("update table set views=views+1 where id=$row[id]");
}
mysql("commit");
Dadurch werden alle Zeilen durchlaufen, die aktualisiert werden müssen. Es funktioniert perfekt, wenn die Anzahl der Server gering ist, wie etwa 4, aber wenn es auf 10+ ansteigt, hängen die Updates auf einmal im Status "Aktualisieren". Nichts sagt, dass es auf ein Schloss wartet, es ist nur "Aktualisieren". Dies geschieht für ungefähr 5 Minuten, wo es schließlich die Aktualisierungen vornimmt und die Schleife durchläuft und schließlich wieder passiert.
Ich suche nicht nach alternativen Möglichkeiten, um die Updates durchzuführen. Dinge wie eine tmp-Tabelle haben und
update table,tmp_table set table.views = table.views+tmp_table.views where
table.id = tmp_table.id
Sperren Sie alle Zeilen, die aktualisiert werden, bis sie alle fertig sind (dies können Stunden sein), was für mich nicht funktioniert. Sie müssen in diesen schrecklichen Schleifen sein.
Ich frage mich, warum sie im Status "Aktualisieren" stecken bleiben könnten und was ich tun kann, um dies zu verhindern.
tldr; Wenn mehr als 10 "Aktualisierungs" -Schleifen vorhanden sind, werden schließlich alle Aktualisierungen gleichzeitig aus einem unbekannten Grund gesperrt, bis sie sich entschließen, endgültig Aktualisierungen vorzunehmen und die Schleifen zu durchlaufen, damit sie Sekunden später erneut ausgeführt werden.
VARIABLEN ANZEIGEN: http://pastebin.com/NdmAeJrz
SHOW ENGINE INNODB STATUS: http://pastebin.com/Ubwu4F1h
quelle
Antworten:
Ich stimme dir nicht zu.
Die Stärke eines RDBMS liegt in der Ausführung von Set-Operationen wie "Alle diese Zeilen aktualisieren plz". Vor diesem Hintergrund sollte Ihre Intuition Ihnen sagen, dass diese "schrecklichen Schleifen" nur unter sehr seltenen Umständen der beste Weg sind.
Werfen wir einen Blick auf Ihre aktuelle Update-Logik und verstehen, was sie tut.
Zunächst einmal ist die
set autocommit=0
Zeile in Ihrem Skript nicht erforderlich . Da Sie eine Transaktion unmittelbar danach explizit mit öffnenstart transaction
, wird sieautocommit
automatisch deaktiviert, bis Sie die Transaktion mitCOMMIT
oder beendenROLLBACK
.Nun zum Kern der Logik: Sie haben alle diese einzelnen Aktualisierungen in einer großen Transaktion in die Schleife eingeschlossen. Wenn Ihre Absicht hinter den iterativen Aktualisierungen darin bestand, die Sperrung zu verringern und die Parallelität zu erhöhen, wird diese Absicht durch die umschlossene Transaktion zunichte gemacht. MySQL muss Sperren für jede Zeile beibehalten, die aktualisiert wird, bis die Transaktion festgeschrieben wird, damit alle auf einmal zurückgesetzt werden können, wenn die Transaktion fehlschlägt oder abgebrochen wird. Darüber hinaus statt im Voraus zu wissen , dass es im Begriff ist , diesen Bereich von Zeilen zu sperren (die MySQL Problem Sperren mit der entsprechenden Granularität ermöglichen würde) der Motor eine große Anzahl von Zeilensperren in Schnellfeuern auszustellen gezwungen wird. Angesichts der Tatsache, dass Sie 1 Million Zeilen aktualisieren, ist dies eine massive Belastung für die Engine.
Ich schlage zwei Lösungen vor:
Aktivieren
autocommit
und entfernen Sie den Transaktions-Wrapper. MySQL kann dann jede Zeilensperre sofort nach Abschluss der Aktualisierung der Zeile aufheben. Es ist immer noch gezwungen, in kurzer Zeit eine große Anzahl von Sperren herauszugeben und freizugeben, daher bezweifle ich, dass dies eine angemessene Lösung für Sie ist. Wenn in der Mitte der Schleife ein Fehler auftritt, wird nichts zurückgesetzt, da die Arbeit nicht transaktionsgebunden ist.Stapeln Sie Ihre Updates in einer temporären Tabelle. Sie haben diese Lösung erwähnt und dann abgelehnt, aber ich wette, sie wird am besten funktionieren. Hast du es schon versucht? Ich würde zuerst das vollständige Millionen-Zeilen-Update testen. Wenn dies zu lange dauert, stapeln Sie die Arbeit in immer kleinere Teile, bis Sie den Sweet Spot gefunden haben: Die Stapel sind groß genug, um die gesamte Arbeit schnell zu erledigen, aber keine einzelne Charge blockiert andere Prozesse zu lange. Dies ist eine gängige Technik, die DBAs verwenden, wenn sie während Live-Vorgängen eine große Anzahl von Zeilen ändern müssen. Denken Sie daran, da Sie Ihr Ziel der gemeinsame Zugriff maximiert ist, halten
autocommit
auf und nicht wickeln jede dieser Arbeit in eine massive Transaktion so MySQL veröffentlicht seine Schleusen so schnell wie möglich.Beachten Sie, dass sich diese Lösung mit zunehmender Verkleinerung der Chargen möglicherweise der ersten annähert. Aus diesem Grund bin ich zuversichtlich, dass diese Lösung eine bessere Leistung erbringen wird: Wenn das Datenbankmodul seine Arbeit in Blöcke gruppieren kann, fliegt es.
quelle
Selbst bei InnoDB besteht immer die unmittelbare Gefahr eines Deadlocks. In diesem speziellen Fall kann ich sogar in InnoDB Zeilen sehen, die kopfüber in Deadlock-Situationen ausgeführt werden, da Sie Daten über den PRIMARY KEY der View-Tabellen aktualisieren. Dadurch wird eine aggressive Sperre innerhalb des Clustered-Index eingeleitet.
Sie können dies mit gesperrt sehen
SHOW ENGINE INNODB STATUS\G
Ich beantwortete drei sehr schwierige Fragen zu einem ähnlichen Thema.
SELECT / UPDATE-Abfragen können beim Aktualisieren über den PRIMARY KEY Sperren für den gen_clust_index , auch bekannt als Clustered Index, durchführen.
Hier sind drei Fragen zum DBA- Stapelaustausch , die ich mit @RedBlueThing , der Person, die diese Fragen gestellt hat, aggressiv durchgesehen habe . @RedBlueThing hat Workarounds für seine Fragen gefunden.
In allen drei dieser Fragen umfasste eine Zeilensperre eine entsprechende Sperre im Clustered-Index derselben Tabelle. Benachbarte Schlüssel gesperrter Zeilen waren beteiligt und trugen somit zu den Problemen bei.
Moral der Geschichte: Deadlocks mit InnoDB sind immer noch eine Möglichkeit. Das Einrichten eines geeigneten Algorithmus für einzelne Sperren auf Zeilenebene und das individuelle Aktualisieren der betreffenden Zeilen ist viel sicherer als das tägliche Massenaktualisieren über mehrere Sperren auf Zeilenebene.
Stellen Sie sicher, dass Sie
autocommit=1
eine Tabelle auf diese Weise stark aktualisieren. Trotzdem führt das Aktualisieren einer Zeile in InnoDB dazu, dass alle Arten von MVCC- Daten um den vorherigen Inhalt der Zeile gelegt werden, um gleichzeitige Transaktionen zu ermöglichen. Aufgrund der Art des UPDATE werden viele MVCC-Daten generiert.quelle
Wenn ich mir Ihren Innodb-Status ansehe, sehe ich, dass der neueste Deadlock mit der Ansichtstabelle auch auf diese Abfrage zurückzuführen ist:
Ist
reddit_new.date
indiziert? Sind die Hash-Spalten aus beiden Tabellen indiziert?quelle