Mises à jour d'index uniques et compteurs de modification des lignes de statistiques

14

Étant donné le tableau suivant, un index cluster unique et des statistiques:

CREATE TABLE dbo.Banana
(
    pk integer NOT NULL, 
    c1 char(1) NOT NULL, 
    c2 char(1) NOT NULL
);

CREATE UNIQUE CLUSTERED INDEX pk ON dbo.Banana (pk);

CREATE STATISTICS c1 ON dbo.Banana (c1);
CREATE STATISTICS c2 ON dbo.Banana (c2);

INSERT dbo.Banana 
    (pk, c1, c2) 
VALUES 
    (1, 'A', 'W'), 
    (2, 'B', 'X'), 
    (3, 'C', 'Y'), 
    (4, 'D', 'Z');

-- Populate statistics
UPDATE STATISTICS dbo.Banana;

Exemples de données

Les compteurs de modification des lignes de statistiques affichent évidemment zéro avant toute mise à jour:

-- Show statistics modification counters
SELECT
    stats_name = S.[name], 
    DDSP.stats_id,
    DDSP.[rows],
    DDSP.modification_counter
FROM sys.stats AS S
CROSS APPLY sys.dm_db_stats_properties(S.object_id, S.stats_id) AS DDSP
WHERE
    S.[object_id] = OBJECT_ID(N'dbo.Banana', N'U');

Compteurs zéro modification

Incrémenter chaque pkvaleur de colonne d'une unité pour chaque ligne:

-- Increment pk in every row
UPDATE dbo.Banana 
SET pk += 1;

Utilise le plan d'exécution:

Plan d'exécution de Split Tri Collapse

Il produit les compteurs de modification de statistiques suivants:

Compteurs de modification post-mise à jour

Des questions

  1. Que font les opérateurs Fractionner, Trier et Réduire?
  2. Pourquoi les pkstatistiques montrent-elles 2 modifications, c1et en c2montrent-elles 5?
Paul White 9
la source

Réponses:

15

SQL Server utilise toujours la combinaison d'opérateurs Split, Sort et Collapse lors de la gestion d'un index unique dans le cadre d'une mise à jour qui affecte (ou pourrait affecter) plusieurs lignes.

En examinant l'exemple de la question, nous pourrions écrire la mise à jour en tant que mise à jour à une seule ligne pour chacune des quatre lignes présentes:

-- Per row updates
UPDATE dbo.Banana SET pk = 2 WHERE pk = 1;
UPDATE dbo.Banana SET pk = 3 WHERE pk = 2;
UPDATE dbo.Banana SET pk = 4 WHERE pk = 3;
UPDATE dbo.Banana SET pk = 5 WHERE pk = 4;

Le problème est que la première instruction échouerait, car elle passe pkde 1 à 2, et il y a déjà une ligne où pk= 2. Le moteur de stockage SQL Server requiert que les index uniques restent uniques à chaque étape du traitement, même dans une seule instruction . Il s'agit du problème résolu par Split, Sort et Collapse.

Divisé Divisé

La première étape consiste à diviser chaque instruction de mise à jour en une suppression suivie d'une insertion:

DELETE dbo.Banana WHERE pk = 1;
INSERT dbo.Banana (pk, c1, c2) VALUES (2, 'A', 'W');

DELETE dbo.Banana WHERE pk = 2;
INSERT dbo.Banana (pk, c1, c2) VALUES (3, 'B', 'X');

DELETE dbo.Banana WHERE pk = 3;
INSERT dbo.Banana (pk, c1, c2) VALUES (4, 'C', 'Y');

DELETE dbo.Banana WHERE pk = 4;
INSERT dbo.Banana (pk, c1, c2) VALUES (5, 'D', 'Z');

L'opérateur Split ajoute une colonne de code d'action au flux (ici intitulée Act1007):

Propriétés divisées

Le code d'action est 1 pour une mise à jour, 3 pour une suppression et 4 pour une insertion.

Trier Trier

Les instructions fractionnées ci-dessus produiraient toujours une fausse violation de clé unique transitoire, donc l'étape suivante consiste à trier les instructions par les clés de l'index unique en cours de mise à jour ( pkdans ce cas), puis par le code d'action. Pour cet exemple, cela signifie simplement que les suppressions (3) sur la même clé sont ordonnées avant les insertions (4). L'ordre résultant est:

-- Sort (pk, action)
DELETE dbo.Banana WHERE pk = 1;
DELETE dbo.Banana WHERE pk = 2;
INSERT dbo.Banana (pk, c1, c2) VALUES (2, 'A', 'W');
DELETE dbo.Banana WHERE pk = 3;
INSERT dbo.Banana (pk, c1, c2) VALUES (3, 'B', 'X');
DELETE dbo.Banana WHERE pk = 4;
INSERT dbo.Banana (pk, c1, c2) VALUES (4, 'C', 'Y');
INSERT dbo.Banana (pk, c1, c2) VALUES (5, 'D', 'Z');

Trier les propriétés

Effondrer Effondrer

L'étape précédente est suffisante pour garantir l'évitement des fausses violations d'unicité dans tous les cas. À titre d'optimisation, Collapse combine des suppressions et des insertions adjacentes sur la même valeur de clé dans une mise à jour:

-- Collapse (pk)
DELETE dbo.Banana WHERE pk = 1;
UPDATE dbo.Banana SET c1 = 'A', c2 = 'W' WHERE pk = 2;
UPDATE dbo.Banana SET c1 = 'B', c2 = 'X' WHERE pk = 3;
UPDATE dbo.Banana SET c1 = 'C', c2 = 'Y' WHERE pk = 4;
INSERT dbo.Banana (pk, c1, c2) VALUES (5, 'D', 'Z');

Les paires suppression / insertion pour les pkvaleurs 2, 3 et 4 ont été combinées dans une mise à jour, laissant une seule suppression sur pk= 1 et une insertion pour pk= 5.

L'opérateur Réduire regroupe les lignes par les colonnes clés et met à jour le code d'action pour refléter le résultat de la réduction:

Réduire les propriétés

Mise à jour de l'index en cluster Mise à jour d'index en cluster

Cet opérateur est étiqueté comme une mise à jour, mais il est capable d'insertions, de mises à jour et de suppressions. L'action entreprise par la mise à jour d'index cluster par ligne est déterminée par la valeur du code d'action dans cette ligne. L'opérateur a une propriété Action pour refléter ce mode de fonctionnement:

Propriété d'action Clustered Index Update


Compteurs de modification de ligne

Notez que les trois mises à jour ci - dessus ne modifient pas la ou les clés de l'index unique en cours de maintenance. En effet, nous avons transformé les mises à jour des colonnes clés de l'index en mises à jour des colonnes non clés ( c1et c2), plus une suppression et une insertion. Ni une suppression ni une insertion ne peuvent provoquer une fausse violation de clé unique.

Une insertion ou une suppression affecte chaque colonne de la ligne, les statistiques associées à chaque colonne verront donc leurs compteurs de modification incrémentés. Pour les mises à jour, seules les statistiques avec l'une des colonnes mises à jour comme colonne de tête voient leurs compteurs de modification incrémentés (même si la valeur est inchangée).

Les compteurs de modification de ligne de statistiques affichent donc 2 changements à pk, et 5 pour c1et c2:

-- Collapse (pk)
DELETE dbo.Banana WHERE pk = 1;                         -- All columns modified
UPDATE dbo.Banana SET c1 = 'A', c2 = 'W' WHERE pk = 2;  -- c1 and c2 modified
UPDATE dbo.Banana SET c1 = 'B', c2 = 'X' WHERE pk = 3;  -- c1 and c2 modified
UPDATE dbo.Banana SET c1 = 'C', c2 = 'Y' WHERE pk = 4;  -- c1 and c2 modified
INSERT dbo.Banana (pk, c1, c2) VALUES (5, 'D', 'Z');    -- All columns modified

Remarque: seules les modifications appliquées à l' objet de base (segment de mémoire ou index cluster) affectent les compteurs de modification des lignes de statistiques. Les index non groupés sont des structures secondaires, reflétant les modifications déjà apportées à l'objet de base. Ils n'affectent pas du tout les compteurs de modification des lignes de statistiques.

Si un objet a plusieurs index uniques, une combinaison séparée de séparation, tri, réduction est utilisée pour organiser les mises à jour de chacun. SQL Server optimise ce cas pour les index non clusterisés en enregistrant le résultat du fractionnement dans un spouleur de table désireux, puis en relisant cet ensemble pour chaque index unique (qui aura ses propres clés Trier par index + code d'action et Réduire).

Effet sur les mises à jour des statistiques

Les mises à jour automatiques des statistiques (si activées) se produisent lorsque l'optimiseur de requêtes a besoin d'informations statistiques et constate que les statistiques existantes sont obsolètes (ou invalides en raison d'un changement de schéma). Les statistiques sont considérées comme obsolètes lorsque le nombre de modifications enregistrées dépasse un seuil.

L'arrangement Split / Sort / Collapse entraîne l' enregistrement de modifications de ligne différentes de celles attendues. Ceci, à son tour, signifie qu'une mise à jour des statistiques peut être déclenchée tôt ou tard que ce ne serait le cas autrement.

Dans l'exemple ci-dessus, les modifications de ligne pour la colonne clé augmentent de 2 (la modification nette) plutôt que de 4 (une pour chaque ligne de tableau affectée), ou 5 (une pour chaque suppression / mise à jour / insertion produite par la réduction).

En outre, les colonnes non clés qui n'ont pas été logiquement modifiées par la requête d'origine accumulent des modifications de ligne, qui peuvent compter jusqu'à deux fois les lignes de table mises à jour (une pour chaque suppression et une pour chaque insertion).


Le nombre de modifications enregistrées dépend du degré de chevauchement entre les anciennes et les nouvelles valeurs de la colonne clé (et donc du degré auquel les suppressions et les insertions peuvent être réduites). En réinitialisant la table entre chaque exécution, les requêtes suivantes montrent l'effet sur les compteurs de modification de lignes avec différents chevauchements:

UPDATE dbo.Banana SET pk = pk + 0; -- Full overlap

pk = pk + 0

UPDATE dbo.Banana SET pk = pk + 1;

pk = pk + 1

UPDATE dbo.Banana SET pk = pk + 2;

pk = pk + 2

UPDATE dbo.Banana SET pk = pk + 3;

pk = pk + 3

UPDATE dbo.Banana SET pk = pk + 4; -- No overlap

pk = pk + 4

Paul White 9
la source