web-dev-qa-db-fra.com

Reconstruire un très grand index de clé primaire

J'ai une base de données SQL hébergée sur Azure. Le problème est que la taille devient hors de contrôle, je peux voir jusqu'à 99% de fragmentation dans les index clusterisés de la clé primaire.

Je suis capable de reconstruire tous les autres index avec l'option online=on Et cela n'affectera pas les performances. La taille de l'un des index PK Clustered est supérieure à 200 Go, et pour celui-ci une REBUILD...WITH (ONLINE=ON) provoque le verrouillage.

Nous avons des utilisateurs de tous les fuseaux horaires qui accèdent au site, donc vraiment, je ne peux pas trouver un moment où je peux reconstruire l'index hors ligne.

Quelle est la meilleure stratégie pour reconstruire de gros index sans avoir de temps d'arrêt sur le site?

Je pense que la réorganisation n'aidera pas car la fragmentation est de 99%. Le problème est que la table est verrouillée même en ligne. Le principal problème est que l'indice est supérieur à 200 Go. La clé primaire est un entier.

13
Techy

Même s'il est un peu tard, je vais répondre à une question dans l'espoir que cela aide ou au moins repousse quelques idées/commentaires supplémentaires sur cette question, car je pense que c'est une bonne question.

Tout d'abord, et je ne sais pas si vous faites cela ou non, mais ne supposez pas que des niveaux de fragmentation élevés sur l'index entraîneront toujours de mauvaises performances. Les statistiques obsolètes (par exemple sys.dm_db_stats_properties ) et les quantités élevées d'espace blanc par page (c'est-à-dire colonne avg_page_space_used_in_percent dans sys.dm_db_index_physical_stats dmv ) sont plus pertinentes en termes de performances que la fragmentation seule. Oui, les index hautement fragmentés généreront plus lectures anticipées et vous voyez généralement des statistiques obsolètes et des niveaux plus élevés d'espace blanc par page couplés à la fragmentation, mais la fragmentation n'est pas directement liée aux optimisations du plan de requête ni comment beaucoup de mémoire en chargeant l'index à partir du disque consommera réellement. Les plans de requête sont affectés par les statistiques et votre l'empreinte mémoire est gonflée avec plus d'espace blanc. Par exemple, un indice fragmenté à 99% mais dont la moyenne est inférieure à 5%. les espaces blancs et les statistiques à jour ne vous causent probablement pas de problèmes de performances drastiques par rapport à un mauvais plan d'exécution en raison de statistiques obsolètes ou à la pagination constante d'un index trop grand pour tenir pleinement en mémoire car il y a une quantité importante d'espace blanc présent par page.

Si la fragmentation est vraiment un problème , vous pouvez la réduire, EN LIGNE, en émettant un ALTER INDEX ... REORGANIZE déclaration identifiée par Dan Guzman dans les commentaires. Cela ne créera pas un index aussi rationalisé qu'une opération REBUILD, mais cela réduira votre fragmentation. La clé ici est d'identifier les fenêtres de moindre utilisation sur votre base de données et de l'exécuter ensuite. Cela peut durer 15 minutes ou plusieurs heures, évidemment plus c'est long, mieux c'est, mais la clé ici est que cette opération ne revient pas en arrière et conserve toute progression même si vous la tuez en cours d'exécution.

Si, dans un monde parfait où votre fragmentation a été éliminée, serait-il plus judicieux d'utiliser le partitionnement sur cette table? Azure SQL Database does permettre pour le partitionnement de table et Microsoft a un excellent article décrivant certains Stratégies de partitionnement pour Azure SQL Database . Si vos données ne sont pas volatiles, le partitionnement peut aider à réduire les besoins de maintenance, et s'il est associé à compression de table , vous pouvez même être en mesure de réduire votre empreinte de stockage globale également. Alberto Murillo réponse précédente fait allusion à l'utilisation Partitionnement horizontal basé sur une région de données, et cette approche peut vous aider à créer des fenêtres de maintenance car vos données seraient plus spécifiques à la région au lieu de global.

La transition vers une table partitionnée ne sera pas facile avec votre absence actuelle de fenêtres de maintenance, mais vous pourrez peut-être utiliser une approche décrite par Maria Zakourdaev qui utilise Vues partitionnées sur le haut de votre table actuelle et une nouvelle table partitionnée pour commencer le partitionnement des données futures. Au fil du temps (et, espérons-le, vos anciennes données sont purgées), vous pouvez éventuellement passer complètement à la table partitionnée. Encore une fois, je ne connais pas vos données ou votre application, mais cette approche est peut-être quelque chose que vous pouvez utiliser.

9
John Eisbrener

Tout d'abord, il est important de déterminer si la fragmentation est importante.

Si votre requête ne fait que des recherches sur une seule ligne, vous ne remarquerez peut-être pas du tout la fragmentation. Sur les SAN modernes, la mise en cache au niveau du SAN peut rendre les E/S phyiscales assez rapides pour que la fragmentation n'ait pas d'importance. Sur SSD, le motif aléatoire IO provoqué par l'analyse d'un index fragmenté peut en fait entraîner de meilleures performances que non fragmenté Les données.

Souvent, les utilisateurs remarquent que la reconstruction d'un index a résolu un problème de performances. La reconstruction d'un index génère également de nouvelles statistiques. Il se peut que le vrai correctif soit de nouvelles statistiques, et non la reconstruction de l'index. UPDATE STATISTICS...WITH FULLSCAN Peut être un moyen moins coûteux, plus rapide et moins intrusif de résoudre le même problème de performances.

Si vous ne rencontrez pas de problèmes causés par la fragmentation, vous pourriez dépenser beaucoup de temps et d'efforts sans gain réel.

Deuxièmement, il existe deux types de fragmentation:

  1. Fragmentation physique. C'est ce à quoi la plupart des gens pensent lorsqu'ils pensent à la fragmentation. Les pages sont hors service et doivent être réorganisées. Lorsque numérisation un index, ce type de fragmentation peut parfois être un problème. J'ai généralement remarqué que cela avait le plus grand impact sur les performances avec les lectures physique. Si vous regardez les résultats de sys.dm_db_index_physical_stats, Ce nombre est la colonne avg_fragmentation_in_percent.

  2. Fragmentation de faible densité. Cette fragmentation est causée par des pages qui ne sont que partiellement remplies de données. Vous avez faible densité de données car vos données sont réparties sur plus de pages que nécessaire. Par conséquent, la lecture des données nécessite plus d'E/S car les données sont réparties sur plus de pages que nécessaire. Cela peut affecter les lectures logiques et physiques. Si vous regardez les résultats de sys.dm_db_index_physical_stats, Ce nombre est la colonne avg_page_space_used_in_percent. Cette colonne n'est remplie que lorsque vous utilisez le mode SAMPLED ou DETAILED.

Alors, que faites-vous à ce sujet:

Fragmentation physique : Si vous recherchez simplement des chiffres élevés pour avg_fragmentation_in_percent, Considérez vraiment si vous perdez votre temps. Assurez-vous que votre requête ne fonctionne pas correctement et utilisez un environnement de test pour confirmer que vous résolvez un problème en éliminant la fragmentation.

Vous pouvez résoudre la fragmentation physique en faisant ALTER INDEX...REORGANIZE. L'opération REORGANIZE est en ligne, déplaçant les pages une par une pour les réorganiser dans l'ordre physique. Si vous tuez une instruction REORGANIZE en cours de route, tout travail déjà effectué est conservé - seule la page actuellement déplacée sera annulée. Faire un REORGANIZE sur une grande table très fragmentée peut nécessiter plus d'espace total dans le journal des transactions et, en mode de récupération complète, peut générer une quantité importante de sauvegardes du journal des transactions. Il peut également prendre plus de temps pour REORGANIZE un index hautement fragmenté que pour REBUILD lui.

Vous verrez souvent des conseils pour effectuer un REBUILD sur des index hautement fragmentés, plutôt qu'un REORGANIZE - car la reconstruction à partir de zéro peut être plus efficace. Cependant, la réorganisation peut être une opération "plus en ligne" et est parfois préférée, même pour des index très fragmentés.

La fragmentation basse densité ne peut pas être corrigée par REORGANIZE. Il ne peut être corrigé qu'en faisant un ALTER INDEX...REBUILD. En faisant l'index avec ONLINE=ON, Vous devriez pouvoir minimiser le blocage. Cependant, REBUILD doit encore prendre un verrou pendant un moment pour échanger l'ancien index contre le nouvel index. Sur un système très occupé, atteindre ce verrou exclusif peut parfois être un problème. Vous devriez être en mesure de confirmer si vous rencontrez ce problème en utilisant quelque chose comme sp_whoisactive pour examiner le blocage pendant votre reconstruction et en regardant les détails des verrous et des attentes. L'utilisation de l'option WAIT_AT_LOW_PRIORITY Peut être utile si vous savez qu'une période de faible utilisation est à venir et que votre reconstruction peut se "faufiler" pour ce swap lorsque l'activité chute suffisamment bas pour atteindre ce verrou. Notez qu'une opération REBUILD de longue durée va également être une transaction ouverte de longue durée. Les transactions ouvertes de longue durée peuvent avoir leurs propres problèmes, liés à l'utilisation/la réutilisation du journal des transactions. Si vous utilisez la mise en miroir ou des groupes de disponibilité, vous devez également tenir compte du rétablissement du journal des transactions sur le réplica secondaire.

4
AMtwo

Remarque

Après ce commentaire:

Vous perdrez des lignes insérées lors de la copie. Si vous voulez éviter cela en verrouillant la table, vous vous retrouvez avec le même problème que l'OP indiqué dans sa question. De plus, 200 Go ne seront pas gratuits :-) - Marco 5 septembre 2017 à 11:18

... Je vois comment cette approche ne fonctionnera pas.

Je vais laisser cette réponse comme un exemple de ce que ne pas faire.


Si vous disposez de plus de 200 Go d'espace libre sur votre base de données Azure, vous pouvez vous faufiler avec la "reconstruction", en copiant vos données dans une table totalement nouvelle et en les y commandant.

Essayer:

  • écrire votre LiveTable dans un NewTable vide
  • copier le LiveTable dans le NewTable
  • renommer LiveTable en OldTable
  • renommer NewTable en LiveTable

Évidemment, utilisez le nom de votre table au lieu de LiveTable.

2
Oreo

Idéalement, si un index est bien conçu, nous ne devrions pas avoir besoin de jouer avec le mécanisme de verrouillage.

Il me semble que vous devrez accepter le verrouillage pour défragmenter l'index clusterisé. S'il y a de bonnes chances que cela se reproduise, envisagez de repenser l'index clusterisé (il devrait être étroit, unique, statique et en constante augmentation).

Je ne sais pas quelle version de SQL Server vous utilisez, mais vous pouvez essayer ce qui suit en 2012:

  • SET DEADLOCK_PRIORITY LOW - Ceci indique au moteur que la reconstruction de l'index doit être la victime du blocage lorsque/s'il y en a une.

  • MaxDOP = 1 - La valeur MaxDOP limite le nombre total de CPU logiques utilisées en parallèle pour créer l'index (à partir de 2005 - édition Enterprise uniquement).

Vous pouvez également modifier la configuration des verrous de page/ligne, mais je ne le ferais pas sans tester. Vous pourriez simplement aggraver le verrouillage, surtout s'il s'agit d'un index mal conçu.

À partir de 2014, il existe l'option suivante qui indique essentiellement au moteur d'autoriser d'autres sessions et à l'opération d'indexation en ligne d'attendre:

(WAIT_AT_LOW_PRIORITY (MAX_DURATION = 1 MINUTES, ABORT_AFTER_WAIT = SELF))
1
GPep

J'ai utilisé la même approche que Oreo décrite ci-dessus avec beaucoup de succès! La seule chose qui manque, c'est que vous devez exécuter un script de mise à jour après avoir copié les données et effectué le dernier changement de nom.

La mise à jour ressemblera à ceci:

Insert from OldTable into LiveTable
  Where not exists (select From OldTable Where LiveTable.Key = OldTable.Key)

Si Clé est une colonne Identité, vous devez utiliser une approche légèrement différente.

0
SBB