Ich habe eine leistungsbezogene Frage. Angenommen, ich habe einen Benutzer mit dem Vornamen Michael. Nehmen Sie die folgende Abfrage:
UPDATE users
SET first_name = 'Michael'
WHERE users.id = 123
Führt die Abfrage das Update tatsächlich aus, obwohl es auf denselben Wert aktualisiert wird? Wenn ja, wie verhindere ich das?
postgresql
performance
update
postgresql-performance
OneSneakyMofo
quelle
quelle
Antworten:
Aufgrund des MVCC-Modells von Postgres und gemäß den Regeln von SQL
UPDATE
schreibt a eine neue Zeilenversion für jede Zeile, die in derWHERE
Klausel nicht ausgeschlossen ist.Dies wirkt sich direkt und indirekt mehr oder weniger erheblich auf die Leistung aus. "Leere Aktualisierungen" verursachen die gleichen Kosten pro Zeile wie alle anderen Aktualisierungen. Sie lösen Trigger (falls vorhanden) wie jedes andere Update aus, müssen WAL-protokolliert sein und produzieren tote Zeilen, die die Tabelle aufblähen und
VACUUM
wie jedes andere Update mehr Arbeit für später verursachen .Indexeinträge und TOASTed- Spalten, in denen keine der beteiligten Spalten geändert wird, können gleich bleiben, dies gilt jedoch für alle aktualisierten Zeilen. Verbunden:
Es ist fast immer eine gute Idee, solche leeren Updates auszuschließen (wenn tatsächlich die Möglichkeit besteht, dass dies passiert). Sie haben in Ihrer Frage keine Tabellendefinition angegeben (was immer eine gute Idee ist). Wir müssen davon ausgehen,
first_name
dass NULL sein kann (was für einen "Vornamen" nicht überraschend wäre), daher muss die Abfrage einen NULL-sicheren Vergleich verwenden :Wenn
first_name IS NULL
vor dem Update ein Test mit nurfirst_name <> 'Michael'
NULL ergibt, wird die Zeile vom Update ausgeschlossen. Hinterhältiger Fehler. Wenn die Spalte definiert istNOT NULL
, verwenden Sie die einfache Gleichheitsprüfung, da dies etwas billiger ist.Verbunden:
quelle
Indexes entries and TOASTed columns where none of the involved columns are changed can stay the same
Aber müssten sie nicht aktualisiert werden, um auf die neue Position der Zeile zu verweisen?rollback
, Snapshot-Handling, Lock-Management, WAL, und was nicht ...ORMs wie Ruby on Rail bieten eine verzögerte Ausführung an, bei der ein Datensatz als geändert markiert wird (oder nicht) und die Änderung dann bei Bedarf oder Aufruf an die Datenbank übermittelt wird.
PostgreSQL ist eine Datenbank und kein ORM. Es hätte die Leistung verringert, wenn es einige Zeit gedauert hätte, zu überprüfen, ob ein neuer Wert mit dem aktualisierten Wert in Ihrer Abfrage übereinstimmt.
Daher wird der Wert unabhängig davon aktualisiert, ob er dem neuen Wert entspricht oder nicht.
Wenn Sie dies verhindern möchten, können Sie Code verwenden, wie ihn Max Vernon in seiner Antwort vorgeschlagen hat.
quelle
Sie könnten einfach die
where
Klausel hinzufügen :Wenn
first_name
als definiert istNOT NULL
, kann dasOR first_name IS NULL
Teil entfernt werden.Die Bedingung:
kann auch eleganter geschrieben werden als (in Erwins Antwort):
quelle
NULL
@erwinAus Datenbanksicht
Die Antwort auf Ihre Frage lautet JA. Das Update wird durchgeführt. Die Datenbank überprüft nicht den vorherigen Wert, sondern setzt nur den neuen Wert.
Da dies im Speicher geschieht (und erst in die Datendateien geschrieben wird, nachdem ein Commit ausgegeben wurde), ist die Leistung kein Problem.
Aus Sicht des ORM
Normalerweise haben Sie ein Objekt, das eine einzelne Zeile der Datenbank darstellt (es kann sehr viel komplexer sein, aber lassen Sie es uns einfach halten). Dieses Objekt wird im Arbeitsspeicher verwaltet (auf App-Serverebene), und nur die letzte festgeschriebene Version dieses Objekts gelangt zu einem bestimmten Zeitpunkt tatsächlich in die Datenbank.
Das könnte das unterschiedliche Verhalten erklären.
Vergleichen wir nun ein Frachtschiff nicht mit einem 3D-Drucker. Die Tatsache, dass Sie 3D-Drucker mit Frachtschiffen versenden können, bedeutet nicht, dass es einen Vergleich zwischen ihnen geben könnte.
Genießen!
Ich hoffe das hat einige Konzepte geklärt.
quelle