A che punto un database aggiorna i suoi indici in una transazione?


11

Sto cercando di comprendere la sequenza di eventi negli inserti in cui sono coinvolti sia un indice che una transazione.

Ad esempio, la documentazione Oracle afferma:

Se si crea [o si dispone] di uno o più indici prima di caricare i dati, il database deve aggiornare ogni indice man mano che viene inserita ogni riga.

Ma cosa succede se creo una transazione, inserisco cinque righe e quindi eseguo il commit? Gli indici vengono aggiornati per ogni inserimento o solo nel punto di commit?

La logica mi dice che sarebbero stati aggiornati solo nel punto di commit, perché un indice aggiornato non poteva essere utile fino a quando questi record non fossero stati sottoposti a commit. Ma è vero?

In tal caso, quando ho 1m di righe da inserire, per prestazioni ottimali dovrei eseguire un commit di tutte le righe e non 10 transazioni da 100k record? Ovviamente mi rendo conto che questo rischia un rollback maggiore se la riga 999.999 fallisce.

Mi scuso se la mia terminologia è un po 'fuori. Non sono un DBA dal commercio. Non sono molto interessato a un determinato database, come ai database in generale, sebbene Oracle e Postgres siano quelli che utilizzo di più. Ho cercato su questo argomento ma non riesco davvero a trovare una risposta definitiva.

Risposte:


8

Lavoro con SQL Server e Oracle. Probabilmente ci sono alcune eccezioni, ma per quelle piattaforme la risposta generale è che i dati e gli indici verranno aggiornati contemporaneamente.

Penso che sarebbe utile fare una distinzione tra quando gli indici vengono aggiornati per la sessione proprietaria della transazione e per altre sessioni. Per impostazione predefinita, le altre sessioni non vedranno gli indici aggiornati finché non viene eseguito il commit della transazione. Tuttavia, la sessione proprietaria della transazione vedrà immediatamente gli indici aggiornati.

Per un modo di pensarci, considera a un tavolo con una chiave primaria. In SQL Server e Oracle questo è implementato come indice. La maggior parte delle volte desideriamo INSERTche si verifichi immediatamente un errore in caso di violazione della chiave primaria. Perché ciò accada, l'indice deve essere aggiornato contemporaneamente ai dati. Si noti che altre piattaforme, come Postgres, consentono vincoli differiti che vengono verificati solo al momento del commit della transazione.

Ecco una breve demo di Oracle che mostra un caso comune:

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

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

La seconda INSERTistruzione genera un errore:

Errore SQL: ORA-00001: vincolo univoco (XXXXXX.SYS_C00384850) violato

00001. 00000 - "Violazione del vincolo univoco (% s.% S)"

* Causa: un'istruzione UPDATE o INSERT ha tentato di inserire una chiave duplicata. Per Trusted Oracle configurato in modalità DBMS MAC, è possibile che venga visualizzato questo messaggio se esiste una voce duplicata a un livello diverso.

* Azione: rimuovere la limitazione univoca o non inserire la chiave.

Se si preferisce vedere un'azione di aggiornamento dell'indice di seguito è una semplice demo in SQL Server. Innanzitutto crea una tabella a due colonne con un milione di righe e un indice non cluster sulla VALcolonna:

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);

La query seguente può utilizzare l'indice non cluster poiché l'indice è un indice di copertura per quella query. Contiene tutti i dati necessari per eseguirlo. Come previsto, non vengono restituiti resi.

SELECT *
FROM X_TABLE_IX
WHERE VAL = 'A';

query 1

Ora iniziamo una transazione e aggiorniamo VALper quasi tutte le righe della tabella:

BEGIN TRANSACTION

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

Ecco parte del piano di query per questo:

query 2

Cerchiato in rosso è l'aggiornamento all'indice non cluster. Cerchiato in blu è l'aggiornamento all'indice cluster, che è essenzialmente i dati della tabella. Anche se la transazione non è stata impegnata, vediamo che i dati e l'indice vengono aggiornati durante l'esecuzione della query. Nota che non lo vedrai sempre in un piano a seconda della dimensione dei dati coinvolti e probabilmente di altri fattori.

Con la transazione ancora non impegnata, rivisitiamo la SELECTquery dall'alto.

SELECT *
FROM X_TABLE_IX
WHERE VAL = 'A';

inserisci qui la descrizione dell'immagine

Query Optimizer è ancora in grado di utilizzare l'indice e questa volta stima che verranno restituite 999999 righe. L'esecuzione della query restituisce il risultato previsto.

Era una semplice demo ma, si spera, ha chiarito un po 'le cose.

Per inciso, sono a conoscenza di alcuni casi in cui si potrebbe sostenere che un indice non viene immediatamente aggiornato. Questo viene fatto per motivi di prestazioni e l'utente finale non dovrebbe essere in grado di vedere dati incoerenti. Ad esempio, a volte le eliminazioni non verranno applicate completamente a un indice in SQL Server. Viene eseguito un processo in background e infine pulisce i dati. Puoi leggere informazioni sui dischi fantasma se sei curioso.


Questa è una super risposta - e risponde anche a un'altra cosa che mi chiedevo: se si sarebbe verificata una violazione della chiave primaria (o simile) su Insert o su Commit. Grazie per una risposta così completa.
Segna l'Irlanda il

La domanda correlata (su quando si verificherà una violazione del vincolo) è correlata all'utilizzo o meno di transazioni differite. SQL Server, ad esempio, non ha implementato la transazione differita, quindi tutte le violazioni si verificano alla fine delle istruzioni. Altri DBMS hanno (Postgres per esempio, anche se non per tutti i tipi di vincoli), quindi quando un vincolo viene rinviato, la violazione verrà verificata nella fase di commit della transazione).
ypercubeᵀᴹ

Oracle supporta anche vincoli differiti
BobC

1

La mia esperienza è che 1.000.000 di inserti di riga richiederanno effettivamente più risorse e impiegheranno più tempo a completarsi rispetto a se si utilizzassero inserti batch. Questo potrebbe essere implementato, ad esempio, in 100 inserti di 10.000 righe.

Ciò riduce l'overhead dei batch da inserire e, se un batch ha esito negativo, è un rollback più piccolo.

In ogni caso, per SQL Server esiste un'utilità bcp o il comando BULK INSERT che potrebbe essere utilizzato per eseguire inserimenti batch.

E, naturalmente, puoi anche implementare il tuo codice per gestire questo approccio.


1
In generale, se è necessario inserire un numero elevato di righe in una tabella che necessita di un indice, è probabile che sia più veloce eliminare l'indice, caricare i dati e quindi ricostruire l'indice. Oracle supporta anche un'opzione di caricamento di massa del percorso diretto, usando il suggerimento / * + APPEND * /.
BobC
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.