Comment puis-je ajouter une colonne rowversion à une grande table avec un temps d'arrêt minimal

21

À l'aide de SQL Server 2008 et versions ultérieures, je souhaite ajouter une colonne rowversion à une grande table mais quand je

ALTER TABLE [Tablename]
ADD Rowversion [Rowversion] NOT NULL

Ensuite, le tableau n'est pas disponible pour les mises à jour depuis trop longtemps.

Quelles stratégies puis-je utiliser pour réduire ce temps d'arrêt? Je considérerai n'importe quoi. Le plus simple sera le mieux, bien sûr, mais je considérerai toute stratégie.

Je pense qu'en dernier recours, je pourrais conserver une copie de la table de transfert gérée par des déclencheurs, puis sp_renommer la table de transfert dans la table d'origine. Mais j'espère quelque chose de plus simple / plus facile.

Michael J Swart
la source

Réponses:

26

Envisagez de créer une nouvelle table avec le même schéma plus la colonne rowversion et ajoutez une vue au-dessus des deux tables qui fait une union. Demandez aux utilisateurs d'utiliser la vue et d'écrire à la place des déclencheurs sur les tables et vues sous-jacentes.

Les insertions doivent être envoyées à la nouvelle table, les mises à jour doivent déplacer les données vers la nouvelle table et les suppressions doivent être appliquées aux deux tables.

Effectuez ensuite des déplacements par lots en arrière-plan, en déplaçant autant d'enregistrements à la fois que vous le pouvez vers la nouvelle table. Vous pouvez toujours avoir des problèmes de simultanéité pendant que cela se passe et certains plans d'exécution craptaculaires, mais cela vous permet de rester en ligne pendant que les mouvements se produisent.

Idéalement, vous démarrez le processus un vendredi après-midi pour minimiser l'effet sur les utilisateurs finaux et essayez de le faire avant le lundi matin. Une fois qu'il est en place, vous pouvez modifier la vue pour pointer uniquement vers la nouvelle table, et les plans d'exécution craptaculaires disparaissent. Idéalement.

Pour éviter le déclenchement des déclencheurs lorsque les données sont migrées par lots, examinez le nombre de lignes dans les tables supprimées / insérées dans le déclencheur et ignorez les activités si elles sont proches du nombre de lignes de votre lot.


À la fin, Michael a décidé d'ignorer la vue (et non de la supprimer du tableau d'origine) pour obtenir des plans plus stables. Le compromis tenait essentiellement deux exemplaires du tableau. Il l'a transformé en une série de billets de blog .

Brent Ozar
la source
7

Si vous avez le temps de planifier à l'avance, il existe une solution beaucoup plus facile ... (généralement)

Les longs verrous sont presque certainement causés par des fractionnements de page au niveau de la couche de stockage. Alors forcez-les selon votre propre horaire.

  1. Ajoutez une colonne temporaire NULL avec le type de données VARBINARY(8).
  2. Recherchez le temps disponible dans la base de données pour mettre à jour les lots des enregistrements existants avec une valeur valide pour le champ. ( 0x0000000027F95A5Bpar exemple)
  3. Les mises à jour forceront les fractionnements de page nécessaires et alloueront plus d'espace à la table.
  4. Lorsque vous êtes rattrapé, supprimez la colonne temporaire (ne touche pas le stockage alloué) et ajoutez la colonne rowversion.
  5. Pas de fractionnement de page et un verrou nécessaire seulement assez longtemps pour remplir les valeurs.

J'ai utilisé cela avec succès pour ajouter une colonne rowversion à une table de lignes 150M en moins de 10 minutes.

Attention ... si vous avez une table avec de grands champs varchar (en particulier varchar(max)) SQL Server décide de reconstruire la table au lieu de réutiliser l'espace nouvellement disponible. J'essaie toujours de trouver un moyen de contourner celui-ci.

Scott Lynch
la source
Intéressant, je suppose que je n'ai pas précisé ce que «trop long» signifiait dans ma question. Si> 30 minutes est trop long pour votre scénario et 10 minutes est tolérable, cette solution fonctionnerait. Mon scénario impliquait d'essayer d'atteindre un temps d'arrêt nul ou plus précisément <10 secondes, ce qui est obtenu par la réponse de Brent.
Michael J Swart
1

Si TIMESTAMPvous ajoutez est NULLABLE:

  1. Ajouter une VARBINARY(8)colonne
  2. Remplissez avec des données.

Une fois remplie, dans les instructions SQL consécutives, DROPla VARBINARY(8)colonne que vous venez d'ajouter et de remplir, puis ajoutez la TIMESTAMP NULLcolonne.


Si TIMESTAMPvous ajoutez est NOT NULLABLE:

  1. Ajouter une BINARY(8)colonne
  2. Remplissez avec des données.

Une fois remplie, dans les instructions SQL consécutives, DROPla BINARY(8)colonne que vous venez d'ajouter et remplie et la ADD THE TIMESTAMP NOT NULLcolonne.

Paul White dit GoFundMonica
la source