Trouver la taille non compressée de toutes les tables d'une base de données

12

Dans Dynamics AX, il existe un mécanisme de mise en cache dans lequel les tables peuvent être configurées pour être chargées en mémoire et mises en cache. Ce cache est limité à une certaine quantité de Ko pour éviter les problèmes de mémoire. Le paramètre dont je parle est appelé entiretablecacheet charge toute la table en mémoire dès qu'un seul enregistrement est demandé.

Jusqu'à récemment, nous nous sommes appuyés sur certains scripts pour vérifier la taille des tables qui ont ce paramètre pour voir si la taille de la table est supérieure à cette limite.

Maintenant cependant, la compression entre en jeu et des choses comme sp_spaceused ou sys.allocation_units semblent signaler l'espace réellement utilisé par les données compressées.

De toute évidence, le serveur d'applications fonctionne avec des données non compressées, de sorte que la taille des données sur le disque dans SQL Server n'est pas pertinente. J'ai besoin de la taille réelle des données non compressées.

Je connais sp_estimate_data_compression_savings mais comme son nom l'indique, ce n'est qu'une estimation.
Je préférerais avoir une taille aussi correcte que possible.

La seule façon dont je pouvais penser était un SQL dynamique alambiqué créant des tables non compressées avec la même structure que les tables compressées, insérant les données compressées dans cette table fantôme, puis vérifiant la taille de cette table fantôme.
Inutile de dire que cela est un peu fastidieux et prend du temps pour fonctionner sur une base de données de plusieurs centaines de Go.

Powershell pourrait être une option, mais je ne voudrais pas parcourir toutes les tables pour effectuer une select *sur elles pour vérifier la taille dans le script car cela inonderait simplement le cache et prendrait probablement aussi beaucoup de temps.

En bref, j'ai besoin d'un moyen d'obtenir la taille de chaque table car elle sera une fois non compressée et avec une fragmentation hors de l'équation telle que présentée à l'application, si cela est possible. Je suis ouvert à différentes approches, T-SQL est préféré mais je ne suis pas opposé à Powershell ou à d'autres approches créatives.

Supposons que le tampon dans l'application correspond à la taille des données. Un bigint est toujours la taille d'un bigint, et un type de données de caractère est de 2 octets par caractère (unicode). Les données BLOB prennent également la taille des données, une énumération est fondamentalement un entier et les données numériques sont numériques (38,12), datetime est la taille d'un datetime. De plus, il n'y a pas de NULLvaleurs, elles sont soit stockées sous forme de chaîne vide, 1900-01-01soit zéro.

Il n'y a pas de documentation sur la façon dont cela est implémenté, mais les hypothèses sont basées sur certains tests et les scripts utilisés par les PFE et l'équipe de support (qui ignorent également la compression apparemment, car la vérification est intégrée dans l'application et l'application ne peut pas le dire si les données sous-jacentes sont compressées) qui vérifient également les tailles de table. Ce lien indique par exemple:

Évitez d'utiliser des caches de table entière pour les tables volumineuses (dans AX 2009 sur 128 Ko ou 16 pages, dans AX 2012 sur le paramètre d'application 'taille de cache de table entière' [par défaut: 32 Ko, ou 4 pages]) - passez à la mise en cache des enregistrements à la place.

Tom V - essayez topanswers.xyz
la source
3
C'est hacky, mais peut-être qu'une copie restaurée avec la compression désactivée serait la plus précise. Ensuite, vous testez également des restaurations, ce qui vous fait ressembler à un TOP 1 DBA.
Erik Darling
Croyez que ce serait votre meilleur pari. Il pourrait y avoir des moyens d'essayer de faire le calcul. Combien de lignes multipliées par les types de données en colonnes et les longueurs définies, puis ajoutez les index, etc. Et qui ne voudrait pas être un TOP 1 DBA?
Mike Walsh
SUM (datalength ()) pour toutes les colonnes obtenir la taille des données non compressées?
Tapakah Ua
@sp_BlitzErik Cela pourrait être une réponse au lieu d'un commentaire.
Tom V - essayez topanswers.xyz

Réponses:

7

J'ai besoin de la taille réelle des données non compressées.
...
Je préfère avoir une taille aussi correcte que possible.

Alors que le désir de cette information est certainement compréhensible, obtenir cette information, en particulier dans le contexte de «corriger le plus possible» est plus difficile que tout le monde attend en raison d'hypothèses erronées. Que ce soit en faisant l'idée de la table fantôme non compressée mentionnée dans la question, ou la suggestion de @ sp_BlitzErik dans un commentaire sur la restauration de la base de données et la décompression là pour vérifier, il ne faut pas supposer que la taille de la table non compressée == la taille desdites données en mémoire sur le serveur d'applications:

  1. Sont toutes les lignes de la table étant mises en cache? Ou tout simplement dans une plage? L'hypothèse ici est qu'il est tout, et que peut - être correct, mais je me suis dit qu'il devrait au moins mentionner que ce pourrait ne pas être le cas ( à moins que la documentation indique le contraire, mais cela est un point mineur de toute façon, ne voulait pas à ne pas mentionner).

    La question a été mise à jour pour indiquer: oui, toutes les lignes sont mises en cache.

  2. Frais généraux de structure

    1. Côté DB:
      page et surcharge de lignes côté DB: le nombre de lignes qui tiennent sur une page est déterminé par de nombreux facteurs susceptibles de perturber les estimations. Même avec un FILLFACTORde 100 (ou 0), il reste probablement de l'espace inutilisé sur la page car il ne suffit pas pour une ligne entière. Et cela s'ajoute à l'en-tête de page. De plus, si une fonctionnalité d'isolement de capture instantanée est activée, il y aura, je crois, 13 octets supplémentaires par ligne occupés par le numéro de version, ce qui annulera les estimations. Il existe d'autres détails liés à la taille réelle de la ligne (bitmap NULL, colonnes de longueur variable, etc.), mais les éléments mentionnés jusqu'à présent devraient à eux seuls faire le point.
    2. Côté serveur d'applications:
      quel type de collection est utilisé pour stocker les résultats mis en cache? Je suppose que c'est une application .NET, est-ce donc un DataTable? Une liste générique? Un SortedDictionary? Chaque type de collection a une quantité différente d'entendu. Je ne m'attendrais pas à ce que l'une des options reflète nécessairement les frais généraux de page et de ligne du côté DB, en particulier à l'échelle (je suis sûr qu'une petite quantité de ligne pourrait ne pas avoir assez de divers pour avoir de l'importance, mais vous ne recherchez pas de différences en centaines d'octets ou juste quelques ko).
  3. Types de données
    1. Côté DB:
      CHAR/ VARCHARdata est stocké à 1 octet par caractère (en ignorant pour l'instant les caractères à double octet). XMLest optimisé pour ne pas prendre autant d’espace que la représentation du texte l’implique. Ce type de données crée un dictionnaire de noms d'éléments et d'attributs et remplace les références réelles à eux dans le document par leurs ID respectifs (plutôt sympa, en fait). Sinon, les valeurs de chaîne sont toutes en UTF-16 (2 ou 4 octets par "caractère"), tout comme NCHAR/ NVARCHAR. DATETIME2est compris entre 6 et 8 octets. DECIMALest compris entre 5 et 17 octets (selon la précision).
    2. Côté serveur d'application: les
      chaînes (encore une fois, en supposant que .NET) sont toujours UTF-16. Il n'y a pas d'optimisation pour les chaînes 8 bits telles que ce qui VARCHARest valable. MAIS, les chaînes peuvent également être "internées" qui est une copie partagée qui peut être référencée plusieurs fois (mais je ne sais pas si cela fonctionne pour les chaînes dans les collections, ou si oui, si cela fonctionne pour tous les types de collections). XMLpeut ou non être stocké de la même manière dans la mémoire (je devrai le vérifier). DateTimeest toujours 8 octets (comme T-SQL DATETIME, mais pas comme DATE, TIMEou DATETIME2). Decimalest toujours de 16 octets .

Tout cela pour dire: il n'y a pratiquement rien que vous puissiez faire du côté DB pour gagner une taille d'empreinte mémoire même assez précise du côté du serveur d'applications. Vous devez trouver un moyen d'interroger le serveur d'application lui-même, après avoir été chargé avec une table particulière, alors sachez quelle est sa taille. Et je ne sais pas si un débogueur vous permettrait de voir la taille d'exécution d'une collection remplie. Sinon, la seule façon de se rapprocher serait de parcourir toutes les lignes d'un tableau, en multipliant chaque colonne par la taille .NET appropriée (par exemple INT= * 4, VARCHAR= DATALENGTH() * 2, NVARCHAR= DATALENGTH(), XML= 🙃, etc.), mais cela laisse toujours la question des frais généraux de la collection plus chaque élément de la collection.

Étant donné une nouvelle définition dans la question, on pourrait probablement faire la requête suivante pour être assez proche. Et peu importe que la table soit compressée ou non, bien que c'est à chaque personne de déterminer si l'analyse de toutes les lignes est appropriée en production (peut-être à partir d'une restauration ou pendant les heures creuses):

SELECT
   SUM( DATALENGTH([NVarcharColumn_1]) + DATALENGTH([NVarcharColumn_N]) ) + 
   SUM( (DATALENGTH([VarcharColumn_1]) + DATALENGTH([VarcharColumn_N])) * 2 ) + 
   SUM(4 * [number_of_INT_columns]) +
   SUM(8 * [number_of_BIGINT_and_DATETIME_columns]) +
   SUM(16 * [number_of_DECIMAL/NUMERIC_and_UNIQUEIDENTIFIER_columns]) +
   etc..
FROM [SchemaName].[TableName] WITH (NOLOCK) -- assuming no Snapshot Isolation

Mais rappelez-vous, cela ne tient pas compte des frais généraux de collection ou d'élément de collection. Et je ne sais pas si nous pouvons obtenir cette valeur sans débogueur (ou peut-être quelque chose comme ILSpy, mais je ne le recommande pas car cela pourrait violer le CLUF en fonction des lois locales).

Solomon Rutzky
la source
Nous avons fini par implémenter les contrôles dans le code pour être sûr de la taille du tampon tel qu'il est présenté à l'application.
Tom V - essayez topanswers.xyz
6

D'après votre question, il semble que vous ayez une taille de cache maximale Set que vous ne souhaitiez pas charger dans le cache des tables qui dépassent cette taille. Si c'est vrai, vous n'avez pas besoin de connaître la taille exacte de chaque table. Vous avez juste besoin de savoir si une table est plus grande ou plus petite que la taille maximale du cache S. C'est un problème beaucoup plus facile selon les définitions de colonne et le nombre de lignes de vos tables.

Je suis d'accord avec la grande réponse de Solomon Rutzky en ce sens que regarder des données non compressées n'est pas la voie à suivre et qu'il pourrait être difficile de trouver une bonne approximation de la taille réelle d'une table dans le cache. Cependant, je vais travailler dans le cadre de la question et supposer que vous pouvez développer une formule suffisamment proche en fonction des définitions de colonne pour les types de données statiques et de la longueur réelle de vos colonnes dynamiques.

Si vous avez ce mappage des types de données à la taille du cache, vous devriez être en mesure d'évaluer certaines tables sans même regarder les données qu'elles contiennent:

  1. Si une table n'a que des types de données statiques (pas de chaînes ou d'objets blob), vous pouvez approximer le nombre de lignes en consultant sys.partitionset en calculant la taille de la table à l'aide des définitions de colonne.
  2. Si une table avec beaucoup de lignes contient suffisamment de colonnes de type de données statiques, vous pourrez peut-être la supprimer comme trop grande sans consulter ses données. Par exemple, une table avec 10 millions de lignes et 5 BIGINTcolonnes pourrait avoir la taille de ces données de 10000000 * (8 + 8 + 8 + 8 + 8) = 400 M octets, ce qui pourrait être supérieur à la taille limite de votre cache S. Ce n'est pas grave s'il a aussi un tas de colonnes de chaînes.
  3. Si une table avec quelques lignes est suffisamment petite, vous pourrez peut-être confirmer qu'elle est inférieure à la limite simplement en supposant que chaque type de données dynamiques a la taille maximale possible. Par exemple, une table de 100 lignes avec une BIGINTcolonne et une NVARCHAR(20)colonne ne doit pas dépasser 100 * (8 + 2 * 20) = 4800 octets.
  4. Il peut être vrai que si une table a une taille compressée dans SQL Server qui est plus grande par un certain facteur S, il est très peu probable qu'elle tienne dans le cache. Vous devriez faire des tests pour déterminer si une telle valeur existe.
  5. Vous pourriez avoir de la chance car toutes les colonnes dynamiques contiennent des statistiques à leur sujet. Les statistiques contiennent des informations sur la longueur moyenne et qui peuvent être suffisamment précises pour vos besoins.

Vous devrez peut-être interroger les données des tables qui ne correspondent à aucun des critères ci-dessus. Il existe quelques astuces que vous pouvez utiliser pour minimiser l'impact sur les performances de cela. Je dirais que vous avez ici deux priorités concurrentes: vous appréciez la précision, mais vous ne voulez pas non plus analyser toutes les données de votre base de données. Il peut être possible d'ajouter une sorte de tampon à vos calculs. Je ne sais pas s'il est plus acceptable d'exclure une table légèrement inférieure à la taille maximale du cache Sou d'inclure une table légèrement supérieure à la taille maximale du cache.

Voici quelques idées pour accélérer les requêtes qui examinent les données de table:

  1. Pour les grands tableaux, vous pouvez utiliser TABLESAMPLEà condition que la taille de votre échantillon soit suffisamment grande.
  2. Pour les grandes tables avec une clé en cluster, il peut être utile de les traiter par lots sur la clé en cluster. Malheureusement, je ne connais pas de moyen de calculer un SUM()qui se termine tôt en fonction de la valeur de cet agrégat. Je n'ai vu que ça fonctionner ROW_NUMBER(). Mais vous pouvez numériser les 10 premiers% du tableau, enregistrer la taille des données calculées, numériser les 10% suivants, etc. Pour les tables trop volumineuses pour le cache, vous pouvez peut-être économiser une quantité importante de travail avec cette approche en quittant tôt.
  3. Pour certaines tables, vous aurez peut-être la chance d'avoir des index de couverture sur toutes les colonnes dynamiques. Selon la taille de la ligne ou d'autres facteurs, l'analyse de chaque index à la fois peut être plus rapide que l'analyse d'une table. Vous pouvez également quitter ce processus plus tôt si la taille de la table est trop grande après la lecture d'un index sur une seule colonne.
  4. Les longueurs moyennes de vos colonnes dynamiques peuvent ne pas changer beaucoup au fil du temps. Il peut être pratique d'économiser les longueurs moyennes que vous calculez et d'utiliser ces valeurs dans vos calculs pendant un certain temps. Vous pouvez réinitialiser ces valeurs en fonction de l'activité DML dans les tableaux ou en fonction d'une autre mesure.
  5. S'il est possible d'exécuter des tests sur toutes les tables pour développer un algorithme, vous pourrez peut-être tirer parti des modèles dans les données. Par exemple, si vous traitez des tables en commençant par la plus petite en premier, vous constaterez peut-être qu'une fois que vous avez traité 10 (j'ai créé ce nombre) tables consécutives qui sont trop grandes pour le cache, il est très peu probable que des tables plus grandes tiennent dans le cache. Cela peut être acceptable s'il est correct d'exclure quelques tables qui auraient pu tenir dans le cache.

Je me rends compte que je n'ai inclus aucun code SQL dans cette réponse. Faites-moi savoir s'il serait utile d'écrire du code de démonstration pour l'une des idées que j'ai discutées ici.

Joe Obbish
la source
2
Je n'avais pas pensé à l'approche d'exclure des tables comme ça, j'aime l'approche
Tom V - essayez topanswers.xyz