Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Beeinflusst das SQL-Update seine Unterabfrage während des Aktualisierungslaufs?

** Bearbeitet **

Auswahl aus der Zieltabelle

Ab 13.2.9.8. Unterabfragen in der FROM-Klausel:

Unterabfragen in der FROM-Klausel können einen Skalar, eine Spalte, eine Zeile oder eine Tabelle zurückgeben. Unterabfragen in der FROM-Klausel können keine korrelierten Unterabfragen sein, es sei denn, sie werden in der ON-Klausel einer JOIN-Operation verwendet.

Also, ja, Sie können die obige Abfrage durchführen.

Das Problem

Hier gibt es eigentlich zwei Probleme. Es gibt Parallelität oder die Sicherstellung, dass niemand sonst die Daten unter unseren Füßen verändert. Dies wird mit Verriegelung gehandhabt. Der Umgang mit der tatsächlichen Änderung neuer gegenüber alten Werten wird mit abgeleiteten Tabellen gehandhabt.

Sperren

Im Fall Ihrer obigen Abfrage führt MySQL mit InnoDB zuerst die SELECT-Anweisung aus und erwirbt eine Lesesperre (gemeinsam genutzt) für jede Zeile in der Tabelle einzeln. Wenn Sie eine WHERE-Klausel in der SELECT-Anweisung hätten, würden nur die von Ihnen ausgewählten Datensätze gesperrt, wobei Bereiche dazu führen würden, dass auch Lücken gesperrt werden.

Eine Lesesperre verhindert, dass andere Abfragen Schreibsperren erhalten, sodass Datensätze nicht von woanders aktualisiert werden können, während sie lesegesperrt sind.

Dann erwirbt MySQL eine (exklusive) Schreibsperre für jeden der Datensätze in der Tabelle einzeln. Wenn Sie eine WHERE-Klausel in Ihrer UPDATE-Anweisung hätten, dann wären nur die spezifischen Datensätze schreibgeschützt, und wiederum, wenn die WHERE-Klausel einen Bereich auswählt, dann hätten Sie einen gesperrten Bereich.

Jeder Datensatz, der eine Lesesperre vom vorherigen SELECT hatte, würde automatisch zu einer Schreibsperre eskaliert.

Eine Schreibsperre verhindert, dass andere Abfragen eine Lese- oder Schreibsperre erhalten.

Sie können Innotop verwenden, um dies anzuzeigen, indem Sie es im Sperrmodus ausführen, eine Transaktion starten, die Abfrage ausführen (aber nicht festschreiben), und Sie werden die Sperren in Innotop sehen. Sie können die Details auch ohne Innotop mit SHOW ENGINE INNODB STATUS anzeigen .

Deadlocks

Ihre Abfrage ist anfällig für einen Deadlock, wenn zwei Instanzen gleichzeitig ausgeführt wurden. Wenn Abfrage A Lesesperren erhalten hat, dann Abfrage B Lesesperren erhalten hat, müsste Abfrage A warten, bis die Lesesperren von Abfrage B freigegeben werden, bevor sie die Schreibsperren erwerben kann. Abfrage B gibt die Lesesperren jedoch erst frei, nachdem sie fertig ist, und sie wird erst beendet, wenn sie Schreibsperren erwerben kann. Abfrage A und Abfrage B befinden sich in einer Pattsituation und daher in einem Deadlock.

Daher möchten Sie möglicherweise eine explizite Tabellensperre durchführen, um sowohl die massive Menge an Datensatzsperren (die Speicherplatz verbrauchen und die Leistung beeinträchtigen) als auch einen Deadlock zu vermeiden.

Ein alternativer Ansatz ist die Verwendung von SELECT ... FOR UPDATE für Ihr inneres SELECT. Dies beginnt mit Schreibsperren für alle Zeilen, anstatt mit Lesesperren zu beginnen und sie zu eskalieren.

Abgeleitete Tabellen

Für das innere SELECT erstellt MySQL eine abgeleitete temporäre Tabelle. Eine abgeleitete Tabelle ist eine tatsächliche, nicht indizierte Kopie der Daten, die sich in der temporären Tabelle befinden, die automatisch von MySQL erstellt wird (im Gegensatz zu einer temporären Tabelle, die Sie explizit erstellen und zu der Indizes hinzugefügt werden können).

Da MySQL eine abgeleitete Tabelle verwendet, ist dies der temporäre alte Wert, auf den Sie sich in Ihrer Frage beziehen. Mit anderen Worten, hier gibt es keine Magie. MySQL macht es genau so, wie Sie es überall sonst machen würden, mit einem temporären Wert.

Sie können die abgeleitete Tabelle sehen, indem Sie eine EXPLAIN-Anweisung gegen Ihre UPDATE-Anweisung ausführen (unterstützt in MySQL 5.6+).