web-dev-qa-db-fra.com

Combien de partitions devrais-je faire pour mes tables d'index de colonne en cluster? Devrais-je diviser les tables de rowstore aussi?

J'ai un entrepôt de données composé de quatre tables d'indice de colonne en coloramé (CCI) et de neuf tables de rowstore. Ces tables sont utilisées uniquement pour Analytics et les données CCI sont insérées à partir de tables de stadification toutes les 15 minutes. Je cherche à optimiser les performances de la requête en ajoutant des partitions et du tri.

Toutes les requêtes de ces données sont indiquées sur un champ entier avec environ 350 valeurs distinctes. La CCI la plus à gauche dispose de 100 m d'enregistrement et de 125 colonnes. Il y a trois enfants CCCI qui ont ce même champ entier. CCI 2 comporte 15 m d'enregistrement et 150 colonnes, CCI 3 et 4 ont à la fois environ 30 m d'enregistrements et 25 colonnes chacune.

Parmi ces 350 entiers distincts, la distribution du nombre d'enregistrements dans la table la plus à gauche est la suivante:

  • 5% supérieur à 1 m
  • 46% plus élevé que 100k
  • 83% supérieur à 10k

De plus, il existe neuf autres tables de rowstore qui rejoignent également les CCI. Celles-ci ont des inserts de filet, sont des enfants du CCCI et ils contiennent tous le même domaine entier. Ces rameurs ont des volumes d'enregistrement similaires ou plus petits, <10 colonnes chacune, deux contiennent des lobes, et deux mises à jour de masse subissent fréquemment (ces mises à jour sont également prédites sur le champ ID).

Combien de partitions dois-je faire?

Devrais-je diviser les tables de rowstore aussi?

Existe-t-il des considérations importantes que je suis surveillé?

Note concernant le "tri" que j'ai mentionné précédemment:

Un champ de date dans la CCI la plus à gauche est souvent un prédicat secondaire dans ces requêtes. Par conséquent, je suis à la recherche de la récidive que CCI par date toutes les quatre semaines environ en tant que maintenance. Je vais atteindre ce type en laissant tomber le CCI, en ajoutant un indice de rowstore en cluster à la date, supprimant cet index, puis ré-ajoute la CCI avec maxDop = 1. Je cherche également trier l'enfant CCIS par la clé de participation à leur parent.

7
Cyndi Baker

Mise à jour après avoir pris partitionnement allant jusqu'à la production :

Décider la partition droite pour un indice de colonne en cluster (CCI) est un processus très sur mesure. Si les mauvaises partitions sont choisies, les performances et la compression seront pires que dans un schéma non partitionné.

Parce que je partitionais quatre CCI, j'ai choisi la CCI avec les moins enregistrés et avons divisé son compte d'enregistrement de 1 048 576 (la taille idéale de Graphgroup CCI). J'ai utilisé ce quotient comme mon nombre proposé de partitions. Ensuite, j'ai rencontré des questions de compte enregistrement en fonction de ce système pour renvoyer les comptes de rangée réels par partition. Cette étape consistait à s'assurer qu'il y avait une répartition raisonnablement une distribution d'enregistrements entre les partitions. Il y avait. J'ai de la chance.

Un obstacle est apparu: ce processus d'analyse de production m'a aidé à arriver au nombre correct de partitions, mais uniquement pour la production. Mes environnements inférieurs sont beaucoup plus petits que la production. Le niveau choisi de partitionnement a tranché les données si bien que je n'avais pas de groupes de rangs complets du tout. Les bases de données inférieures ont été plus grandes et les temps de requête sont restés les mêmes. IO= a diminué de façon spectaculaire et je devais indiquer à plusieurs reprises que les gains de cette initiative ont été interrogés. Il était difficile de prouver que la partition allait vraiment aider jusqu'à la production.

Le résultat: le partitionnement a été un grand succès de la production. IO======= Mes temps de requête ont été réduits de 70% ou plus. J'ai également plus d'options pour gérer ces tables en petits morceaux.

Certaines notes: Choisissez le champ correct à la partition. Si vous avez des questions qui doivent naviguer dans beaucoup de partitions, vous pouvez trouver des performances dégradées. En outre, j'ai laissé la place pour la croissance, l'ajout de partitions et des gammes à ma fonction de partition pour les données qui ne sont pas là maintenant, mais seront un jour.

Réponse originale d'un seul test local :

Depuis poser cette question, j'ai fait plus de recherches et un POC localement. Il a été suggéré de partager ce POC dans une réponse.

Dans mon POC, j'ai choisi d'utiliser une fonction de partition de:

CREATE PARTITION FUNCTION [MyIntPF](int) 
AS RANGE LEFT 
FOR VALUES (
    N'50'
    , N'100'
    , N'150'
    , N'200'
    , N'250'
    , N'300'
    , N'350'
    , N'400'
    , N'450'
    , N'500'
);

CREATE PARTITION SCHEME [MyIntPS] 
AS PARTITION [MyIntPF] 
TO (
    [MyInt050fg]
    , [MyInt100fg]
    , [MyInt150fg]
    , [MyInt200fg]
    , [MyInt250fg]
    , [MyInt300fg]
    , [MyInt350fg]
    , [MyInt400fg]
    , [MyInt450fg]
    , [MyInt500fg]
    , [MyInt000fg]
);

Cette fonction attribue 50 myignonnes à chaque partition avec une place pour une petite croissance.

N'oubliez pas que j'ai environ 350 mytins distincts dans les enregistrements de 170 m dans le PROD CCIS. David Browne a suggéré une taille d'enregistrement minimale de 1 m dans une partition, ce qui a du sens afin d'optimiser un segment compressé CCI. Je vais errer plus grand pour deux raisons. La première raison est d'éviter de créer un monstre de PoC de 100 partitions. La seconde est que je présume que 1M s'applique à chaque table de la partition. Je partitionne quatre colonnes à quatre colonnes, dont le plus petit a 25 millions de documents. Si je l'ai cassé en 100 pièces, cela n'atteindrait jamais un segment complet.

Dans mon Développement local DB, j'ai 2,2 millions d'enregistrements dans la CCI la plus à gauche, et encore moins que cela dans l'enfant CCIS. Cela pose un problème pour créer une réplication réaliste de prod. Je dois vraiment hiérarchiser un peu de temps supplémentaire pour faire une grande dB locale pour cela, mais dans l'intervalle, voici l'avant/après =IO résultats de la partition locale. J'ai interrogé pour un agrégat de mes plus grands CCI fondé sur myint = une valeur unique.

non partitionné

Numérisation de numérisation 1, Lectures logiques 0, Lectures physiques 0, Lecture à l'avance Lit 0, LOB Logical Lit 1548, [.____] LOB Physical Reads 0, LOB Read-Way-aveugle lit 44. [.____] Segment Reads 4, segment ignoré 0.

partitionné

Numérisation de numérisation 1, Lectures logiques 0, Lectures physiques 0, Read-aveugle Reads 0, LOB Logique lit 268, [.____] LOB Physical Reads 0, LOB Read-Awist lit 0. 
 Segment Reads 1, segment ignoré 0.

Comme prévu, SQL Server a pu ignorer tout sauf une de mes partitions dans une requête avec un prédicat de myint égalité.

Je continue de travailler à ce sujet et aurait dû passer le temps de mettre à jour ici que les choses avancent.

6
Cyndi Baker

Avantages de partitionnement d'une CCI:

  1. La performance de la requête peut être améliorée car un niveau minimum d'élimination du groupe de rangs est garanti, malgré la manière dont les données sont chargées ou modifiées. La plupart des directives génériques de partitionnement du serveur SQL SQL n'entraînent pas cela en compte.

  2. Amélioration de la flexibilité avec les opérations de maintenance en ce que vous pouvez faire des reconstitutions au niveau de la partition ou se réorganiser au niveau de la partition (après la sortie de partition). Vous pouvez également envoyer différentes partitions à différents groupes de fichiers, mais j'ai besoin de vous avertir que cela va presque améliorer la performance. Les groupes de fichiers sont une fonction de maintenance. L'augmentation du nombre de fichiers peut améliorer parfois les performances. En fonction de votre configuration de stockage, vous souhaitez presque certainement que les données pertinentes pour que vos requêtes soient réparties sur plusieurs fichiers pour améliorer les E/S.

  3. L'élimination de la partition couvre plus de scénarios que l'élimination du groupe de rangée sur la même colonne. Par exemple, un filtre de WHERE ID < 0 OR ID > 10 ne sera pas de qualité pour l'élimination du groupe de rangs, mais sera admissible à l'élimination de la partition.

  4. La boucle par partition peut être utile lorsque vous effectuez des opérations de maintenance nécessitant la modification de toutes les lignes. Par exemple, supposons que vous ajoutez une nouvelle colonne à une table pouvant être dérivée de colonnes existantes de cette table. Le partitionnement vous permet de scinder efficacement qui fonctionne en lots si vous le souhaitez.

Downsides de partitionnement d'une CCI:

  1. Sans entretien, le nombre de lignes dans Delta Rowgroups peut augmenter considérablement. Considérez une CCI non sélectionné qui est chargée d'inserts parallèles chez MaxDop 8. Au plus, vous aurez 4194304 rangées dans le magasin Delta. Si la table est modifiée pour avoir 50 partitions, il est maintenant possible d'avoir 209715200 rangées dans le magasin Delta.

  2. Les plans de requête pour les insertions et la suppression dans la colonne peuvent contenir un opérateur de tri comme enfant de l'opérateur DML. Si ce type ne peut pas obtenir suffisamment de mémoire, vous pouvez vous retrouver avec des dégradations extrêmes de performance. Je recommande que la modification d'une partition à la fois si vous utilisez un insert parallèle.

  3. Si vous choisissez votre fonction de partition imprudente, vous pourriez vous retrouver avec des partitions trop petites. Beaucoup de gens vous indiqueront à la limite de rangée 1048576 pour un groupe de rangée comme la taille idéale, mais personnellement, je considère que les avantages d'y aller soient trop détruits. Vous voulez probablement éviter de nombreuses minuscules partitions si vous pouvez l'aider.

  4. Si vous avez trop de partitions dans votre table ou votre base de données, les mauvaises choses peuvent arriver. Malheureusement, ce n'est pas très bien défini et il est difficile de trouver une source crédible pour ce que signifie "trop ​​de partitions". J'ai entendu parler de et avons vu des problèmes avec les temps de compilation de la requête. Il y avait une réponse récente ici à propos de DBCC CHECKTABLE ainsi que.

Appliquer ce qui précède dans votre scénario: avec la ligne compte que vous n'avez pas besoin de rencontrer aucun des cas vraiment graves. Pour les performances de la requête, certaines personnes ont besoin de temps d'exécution de requêtes très rapides et ils doivent sauter autant de groupes de rangs que possible. D'autres ont juste besoin d'un niveau minimum d'élimination du groupe de rangs, car la plupart des travaux effectués dans la requête sont en dehors des analyses de colonne. Cela rend difficile pour quelqu'un à l'extérieur de vous donner une recommandation pour le nombre de partitions. Pour la table de 100 millions, rien de 4-100 pourrait être raisonnable.

Vous pouvez essayer de tester certaines de vos questions avec différents numéros de lignes dans les partitions pour voir comment les changements de performances. Cela peut être simulé en créant des copies des tables ou en créant une fonction de partition sur une table avec une légèreté délibérée et de modifier ce que vous filtrez. Si vous prenez ce qui vous permet de répondre à des performances suffisamment de questions et de vérifier que vous n'aurez aucun problème avec chargement de données, vous devriez être bon.

Les rampes de rangée ne sont pas pertinentes pour la question, ou plutôt, ils sont une question totalement différente. Le partitionnement n'est pas le bon outil pour améliorer les performances sur la requête de RowStore. J'ai vu des gains de performance sur les systèmes simplement en cloisonnant des tables de colonne et en laissant les tables de rowstore seul.

8
Joe Obbish