Ab wann aktualisiert eine Datenbank ihre Indizes in einer Transaktion?

11

Ich versuche, die Abfolge von Ereignissen in Einfügungen zu verstehen, an denen sowohl ein Index als auch eine Transaktion beteiligt sind.

In der Oracle-Dokumentation heißt es beispielsweise:

Wenn Sie vor dem Laden von Daten einen oder mehrere Indizes erstellen (oder haben), muss die Datenbank jeden Index aktualisieren, wenn jede Zeile eingefügt wird.

Aber was passiert, wenn ich eine Transaktion erstelle, fünf Zeilen einfüge und dann festschreibe? Werden die Indizes für jede Einfügung oder nur am Festschreibungspunkt aktualisiert?

Die Logik teilt mir mit, dass sie nur am Festschreibungspunkt aktualisiert werden, da ein aktualisierter Index möglicherweise erst dann von Nutzen sein kann, wenn diese Datensätze festgeschrieben werden. Aber ist das wahr?

Wenn ja, wenn ich 1 Million Zeilen einfügen muss, sollte ich für eine optimale Leistung ein großes Commit aller Zeilen durchführen und nicht 10 Transaktionen mit 100.000 Datensätzen? Natürlich ist mir klar, dass dies einen größeren Rollback riskiert, wenn Zeile 999.999 ausfällt.

Entschuldigung, wenn meine Terminologie ein wenig aus ist. Ich bin kein DBA von Beruf. Ich interessiere mich nicht so sehr für eine bestimmte Datenbank, sondern für Datenbanken im Allgemeinen, obwohl ich Oracle und Postgres am häufigsten benutze. Ich habe nach diesem Thema gesucht, kann aber keine endgültige Antwort finden.

Mark Irland
quelle

Antworten:

8

Ich arbeite mit SQL Server und Oracle. Es gibt wahrscheinlich einige Ausnahmen, aber für diese Plattformen lautet die allgemeine Antwort, dass Daten und Indizes gleichzeitig aktualisiert werden.

Ich denke, dass es hilfreich wäre, zu unterscheiden, wann die Indizes für die Sitzung, der die Transaktion gehört, und für andere Sitzungen aktualisiert werden. Standardmäßig werden in anderen Sitzungen die aktualisierten Indizes erst angezeigt, wenn die Transaktion festgeschrieben wurde. In der Sitzung, der die Transaktion gehört, werden jedoch sofort die aktualisierten Indizes angezeigt.

Betrachten Sie einen Tisch mit einem Primärschlüssel, um darüber nachzudenken. In SQL Server und Oracle wird dies als Index implementiert. Meistens möchten wir, dass sofort ein Fehler auftritt, wenn ein Fehler INSERTauftritt, der den Primärschlüssel verletzt. Dazu muss der Index gleichzeitig mit den Daten aktualisiert werden. Beachten Sie, dass andere Plattformen wie Postgres verzögerte Einschränkungen zulassen, die nur überprüft werden, wenn die Transaktion festgeschrieben wird.

Hier ist eine kurze Oracle-Demo, die einen häufigen Fall zeigt:

CREATE TABLE X_TABLE (PK INT NULL, PRIMARY KEY (PK));

INSERT INTO X_TABLE VALUES (1);
INSERT INTO X_TABLE VALUES (1); -- no commit

Die zweite INSERTAnweisung gibt einen Fehler aus:

SQL-Fehler: ORA-00001: Die eindeutige Einschränkung (XXXXXX.SYS_C00384850) wurde verletzt

00001. 00000 - "eindeutige Einschränkung (% s.% S) verletzt"

* Ursache: Eine UPDATE- oder INSERT-Anweisung hat versucht, einen doppelten Schlüssel einzufügen. Bei Trusted Oracle, das im DBMS MAC-Modus konfiguriert ist, wird diese Meldung möglicherweise angezeigt, wenn auf einer anderen Ebene ein doppelter Eintrag vorhanden ist.

* Aktion: Entfernen Sie entweder die eindeutige Einschränkung oder stecken Sie den Schlüssel nicht ein.

Wenn Sie eine Indexaktualisierungsaktion bevorzugen, finden Sie unten eine einfache Demo in SQL Server. Erstellen Sie zunächst eine zweispaltige Tabelle mit einer Million Zeilen und einem nicht gruppierten Index für die VALSpalte:

DROP TABLE IF EXISTS X_TABLE_IX;

CREATE TABLE X_TABLE_IX (
ID INT NOT NULL,
VAL VARCHAR(10) NOT NULL
PRIMARY KEY (ID)
);

CREATE INDEX X_INDEX ON X_TABLE_IX (VAL);

-- insert one million rows with N from 1 to 1000000
INSERT INTO X_TABLE_IX
SELECT N, N FROM dbo.Getnums(1000000);

Die folgende Abfrage kann den nicht gruppierten Index verwenden, da der Index ein Deckungsindex für diese Abfrage ist. Es enthält alle Daten, die zur Ausführung benötigt werden. Wie erwartet werden keine Rücksendungen zurückgegeben.

SELECT *
FROM X_TABLE_IX
WHERE VAL = 'A';

Abfrage 1

Starten wir nun eine Transaktion und aktualisieren sie VALfür fast alle Zeilen in der Tabelle:

BEGIN TRANSACTION

UPDATE X_TABLE_IX
SET VAL = 'A'
WHERE ID <> 1;

Hier ist ein Teil des Abfrageplans dafür:

Abfrage 2

Rot eingekreist ist die Aktualisierung des nicht gruppierten Index. Blau eingekreist ist die Aktualisierung des Clustered-Index, bei dem es sich im Wesentlichen um die Daten der Tabelle handelt. Obwohl die Transaktion nicht festgeschrieben wurde, werden die Daten und der Index in einem Teil der Ausführung der Abfrage aktualisiert. Beachten Sie, dass dies in einem Plan nicht immer angezeigt wird, abhängig von der Größe der beteiligten Daten und möglicherweise anderen Faktoren.

Da die Transaktion noch nicht festgeschrieben ist, wiederholen wir die SELECTAbfrage von oben.

SELECT *
FROM X_TABLE_IX
WHERE VAL = 'A';

Geben Sie hier die Bildbeschreibung ein

Das Abfrageoptimierungsprogramm kann den Index weiterhin verwenden und schätzt diesmal, dass 999999 Zeilen zurückgegeben werden. Das Ausführen der Abfrage gibt das erwartete Ergebnis zurück.

Das war eine einfache Demo, aber hoffentlich hat es die Dinge ein bisschen geklärt.

Abgesehen davon sind mir einige Fälle bekannt, in denen argumentiert werden könnte, dass ein Index nicht sofort aktualisiert wird. Dies erfolgt aus Leistungsgründen und der Endbenutzer sollte keine inkonsistenten Daten sehen können. Beispielsweise werden Löschvorgänge manchmal nicht vollständig auf einen Index in SQL Server angewendet. Ein Hintergrundprozess wird ausgeführt und bereinigt schließlich die Daten. Sie können über Geisteraufzeichnungen lesen, wenn Sie neugierig sind.

Joe Obbish
quelle
Das ist eine super Antwort - und beantwortet auch eine andere Frage, die ich mich gefragt habe: ob beim Einfügen oder beim Festschreiben eine Verletzung des Primärschlüssels (oder einer ähnlichen) auftreten würde. Vielen Dank für diese vollständige Antwort.
Mark Ireland
Die zugehörige Frage (etwa wann eine Einschränkungsverletzung auftritt) bezieht sich darauf, ob Sie zurückgestellte Transaktionen verwenden oder nicht. SQL Server hat beispielsweise keine verzögerte Transaktion implementiert, sodass alle Verstöße am Ende von Anweisungen auftreten. Andere DBMS haben (z. B. Postgres, jedoch nicht für alle Arten von Einschränkungen). Wenn Sie also eine Einschränkung zurückstellen, wird der Verstoß in der Festschreibungsphase der Transaktion überprüft.
Ypercubeᵀᴹ
Oracle unterstützt auch verzögerte Einschränkungen
BobC
1

Ich habe die Erfahrung gemacht, dass 1.000.000 Zeileneinfügungen tatsächlich mehr Ressourcen erfordern und länger dauern als bei Verwendung von Batch-Einfügungen. Dies könnte beispielsweise in 100 Einfügungen von 10.000 Zeilen implementiert werden.

Dies reduziert den Overhead der eingefügten Stapel und wenn ein Stapel ausfällt, handelt es sich um einen kleineren Rollback.

In jedem Fall gibt es für SQL Server ein Dienstprogramm bcp oder den Befehl BULK INSERT , mit dem Batch-Einfügungen durchgeführt werden können.

Natürlich können Sie auch Ihren eigenen Code für diesen Ansatz implementieren.

RLF
quelle
1
Wenn Sie eine große Anzahl von Zeilen in eine Tabelle einfügen müssen, für die ein Index erforderlich ist, ist es im Allgemeinen wahrscheinlich schneller, den Index zu löschen, die Daten zu laden und dann den Index neu zu erstellen. Oracle unterstützt auch eine Option zum direkten Laden von Pfaden mit direktem Pfad unter Verwendung des Hinweises / * + APPEND * /.
BobC