À quel moment une base de données met-elle à jour ses index dans une transaction?

11

J'essaie de comprendre la séquence d'événements dans les insertions où à la fois un index et une transaction sont impliqués.

Par exemple, la documentation Oracle indique:

Si vous créez [ou avez] un ou plusieurs index avant de charger les données, la base de données doit alors mettre à jour chaque index à mesure que chaque ligne est insérée.

Mais que se passe-t-il si je crée une transaction, insère cinq lignes, puis valide? Les index sont-ils mis à jour pour chaque insertion, ou juste au point de validation?

Logic me dit qu'ils ne seraient mis à jour qu'au point de validation, car un index mis à jour ne pourrait pas être utile jusqu'à ce que ces enregistrements soient validés. Mais est-ce vrai?

Si c'est le cas, lorsque j'ai 1m de lignes à insérer, pour de meilleures performances, dois-je faire une grande validation de toutes les lignes et non 10 transactions de 100k enregistrements? Bien sûr, je me rends compte que cela risque un plus grand retour en arrière si la ligne 999 999 échoue.

Toutes mes excuses si ma terminologie est un peu dépassée. Je ne suis pas un DBA de métier. Je ne suis pas tellement intéressé par une base de données particulière, comme les bases de données en général, bien qu'Oracle et Postgres soient ce que j'utilise le plus. J'ai cherché sur ce sujet mais je ne trouve pas vraiment de réponse définitive.

Mark Ireland
la source

Réponses:

8

Je travaille avec SQL Server et Oracle. Il y a probablement quelques exceptions, mais pour ces plateformes, la réponse générale est que les données et les index seront mis à jour en même temps.

Je pense qu'il serait utile de faire une distinction entre le moment où les index sont mis à jour pour la session propriétaire de la transaction et pour les autres sessions. Par défaut, les autres sessions ne verront pas les index mis à jour tant que la transaction n'est pas validée. Cependant, la session propriétaire de la transaction verra immédiatement les index mis à jour.

Pour une façon d'y penser, pensez à une table avec une clé primaire. Dans SQL Server et Oracle, cela est implémenté en tant qu'index. La plupart du temps, nous voulons qu'il y ait immédiatement une erreur si une action INSERTest effectuée qui violerait la clé primaire. Pour cela, l'index doit être mis à jour en même temps que les données. Notez que d'autres plateformes, telles que Postgres, autorisent des contraintes différées qui ne sont vérifiées que lorsque la transaction est validée.

Voici une démo rapide d'Oracle montrant un cas courant:

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 deuxième INSERTinstruction renvoie une erreur:

Erreur SQL: ORA-00001: contrainte unique (XXXXXX.SYS_C00384850) violée

00001. 00000 - "contrainte unique (% s.% S) violée"

* Cause: une instruction UPDATE ou INSERT a tenté d'insérer une clé en double. Pour Oracle de confiance configuré en mode MAC SGBD, vous pouvez voir ce message si une entrée en double existe à un niveau différent.

* Action: supprimez la restriction unique ou n'insérez pas la clé.

Si vous préférez voir une action de mise à jour d'index ci-dessous est une simple démonstration dans SQL Server. Créez d'abord une table à deux colonnes avec un million de lignes et un index non cluster sur la VALcolonne:

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 requête suivante peut utiliser l'index non cluster car l'index est un index de couverture pour cette requête. Il contient toutes les données nécessaires à son exécution. Comme prévu, aucun retour n'est retourné.

SELECT *
FROM X_TABLE_IX
WHERE VAL = 'A';

requête 1

Maintenant, commençons une transaction et mettons à jour VALpresque toutes les lignes du tableau:

BEGIN TRANSACTION

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

Voici une partie du plan de requête pour cela:

requête 2

Entouré de rouge est la mise à jour de l'index non cluster. La mise à jour de l'index cluster, entourée de bleu, est essentiellement les données de la table. Même si la transaction n'a pas été validée, nous voyons que les données et l'index sont mis à jour dans le cadre de l'exécution de la requête. Notez que vous ne verrez pas toujours cela dans un plan en fonction de la taille des données impliquées et éventuellement d'autres facteurs.

La transaction n'étant toujours pas validée, revoyons la SELECTrequête ci-dessus.

SELECT *
FROM X_TABLE_IX
WHERE VAL = 'A';

entrez la description de l'image ici

L'optimiseur de requêtes est toujours en mesure d'utiliser l'index et cette fois, il estime que 999999 lignes seront renvoyées. L'exécution de la requête renvoie le résultat attendu.

C'était une démo simple mais j'espère que cela clarifie un peu les choses.

Soit dit en passant, je suis au courant de quelques cas où l'on pourrait faire valoir qu'un indice n'est pas immédiatement mis à jour. Cette opération est effectuée pour des raisons de performances et l'utilisateur final ne doit pas être en mesure de voir des données incohérentes. Par exemple, il arrive que des suppressions ne soient pas entièrement appliquées à un index dans SQL Server. Un processus d'arrière-plan s'exécute et finit par nettoyer les données. Vous pouvez lire sur les enregistrements fantômes si vous êtes curieux.

Joe Obbish
la source
C'est une super réponse - et répond également à une autre chose que je me demandais: si une violation de clé primaire (ou similaire) se produirait lors de l'insertion ou de la validation. Merci pour cette réponse si complète.
Mark Ireland
La question connexe (sur le moment où une violation de contrainte se produira) est liée à l'utilisation ou non de transactions différées. SQL Server par exemple, n'a pas implémenté de transaction différée, donc toutes les violations se produisent à la fin des instructions. D'autres SGBD ont (Postgres par exemple, mais pas pour tous les types de contraintes), donc lorsque vous une contrainte est différée, la violation sera vérifiée à la phase de validation de la transaction).
ypercubeᵀᴹ
Oracle prend également en charge les contraintes différées
BobC
1

D'après mon expérience, un insert de 1 000 000 de lignes nécessitera en fait plus de ressources et prendra plus de temps à compléter que si vous utilisiez des insertions par lots. Cela pourrait être implémenté, par exemple, dans 100 insertions de 10 000 lignes.

Cela réduit les frais généraux des lots insérés et, si un lot échoue, il s'agit d'un retour en arrière plus petit.

Dans tous les cas, pour SQL Server, il existe un utilitaire bcp ou la commande BULK INSERT qui pourrait être utilisée pour effectuer des insertions par lots.

Et, bien sûr, vous pouvez également implémenter votre propre code pour gérer cette approche.

RLF
la source
1
En général, si vous devez insérer un grand nombre de lignes sur une table qui a besoin d'un index, il sera probablement plus rapide de supprimer l'index, de charger les données, puis de reconstruire l'index. Oracle prend également en charge une option de chargement en bloc de chemin direct, à l'aide de l'indication / * + APPEND * /.
BobC