Pourquoi l'espace de données d'une table peut-il prendre 4x la taille des données brutes?

18

J'ai une table avec 490 M lignes et 55 Go d'espace table, donc environ 167 octets par ligne. Le tableau comporte trois colonnes: a VARCHAR(100), a DATETIME2(0)et a SMALLINT. La longueur moyenne du texte dans le VARCHARchamp est d'environ 21,5, donc les données brutes doivent être d'environ 32 octets par ligne: 22 + 2 pour le VARCHAR, 6 pour le DATETIME2et 2 pour l'entier 16 bits.

Notez que l'espace ci-dessus est uniquement des données, pas des indices. J'utilise la valeur indiquée sous Propriétés | Stockage | Général | Espace de données.

Bien sûr, il doit y avoir une surcharge, mais 135 octets par ligne semble beaucoup, surtout pour une grande table. Pourquoi est-ce possible? Quelqu'un d'autre a-t-il vu des multiplicateurs similaires? Quels facteurs peuvent influencer la quantité d'espace supplémentaire nécessaire?

À titre de comparaison, j'ai essayé de créer une table avec deux INTchamps et 1 M lignes. L'espace de données requis était de 16,4 Mo: 17 octets par ligne, contre 8 octets de données brutes. Une autre table de test avec un INTet un VARCHAR(100)rempli avec le même texte que le vrai tableau utilise 39 octets par ligne (44 K lignes), où j'attendrais 28 plus un peu.

Ainsi, la table de production a considérablement plus de frais généraux. Est-ce parce que c'est plus grand? Je m'attendrais à ce que la taille des index soit à peu près N * log (N), mais je ne vois pas pourquoi l'espace requis pour que les données réelles soient non linéaires.

Merci d'avance pour tous les pointeurs!

ÉDITER:

Tous les champs répertoriés sont NOT NULL. La vraie table a un PK en cluster sur le VARCHARterrain et le DATETIME2champ, dans cet ordre. Pour les deux tests, le premier INTétait le PK (groupé).

Si cela importe: le tableau est un enregistrement des résultats de ping. Les champs sont URL, date / heure de ping et latence en millisecondes. Les données sont constamment ajoutées et jamais mises à jour, mais les données sont supprimées périodiquement pour les réduire à quelques enregistrements par heure et par URL.

ÉDITER:

Une réponse très intéressante ici suggère que, pour un index avec beaucoup de lecture et d'écriture, la reconstruction peut ne pas être bénéfique. Dans mon cas, l'espace consommé est un problème, mais si les performances d'écriture sont plus importantes, on peut être mieux avec des indices flasques.

Jon de tous les métiers
la source

Réponses:

11

Après discussions dans les commentaires sur la question d'origine, il apparaît dans ce cas que l'espace perdu est provoqué par le choix de la clé en cluster, ce qui a conduit à une fragmentation massive.

Il vaut toujours la peine de vérifier l'état de fragmentation via sys.dm_db_index_physical_stats dans ces situations.

Edit: Suite à la mise à jour dans les commentaires

La densité de page moyenne (avant la reconstruction de l'index clusterisé) était de 24%, ce qui correspond parfaitement à la question d'origine. Les pages n'étaient que 1/4 pleines, donc la taille totale était 4x la taille des données brutes.

Mark Storey-Smith
la source
7

Les structures sur disque ont des frais généraux:

  • en-tête de ligne
  • bitmap nul + pointeur
  • décalages de colonnes de longueur variable
  • pointeurs de version de ligne (facultatif)
  • ...

En prenant 2 x 4 octets int colonnes, vous avez

  • En-tête de ligne de 4 octets
  • Pointeur de 2 octets sur le bitmap NULL
  • 8 octets pour 2 colonnes int
  • Bitmap NULL 3 octets

Wow 17 octets!

Vous pouvez faire de même pour votre deuxième table de test qui a plus de frais généraux que votre original:

  • 2 octets pour le nombre de colonnes de longueur variable
  • 2 octets par colonne de longueur variable

Pourquoi la différence? De plus (je ne lierai pas à ceux-ci)

  • avez-vous déjà reconstruit des index pour les défragmenter?
  • les suppressions ne récupèrent pas d'espace
  • les pages de données se diviseront si vous insérez au milieu
  • les mises à jour peuvent provoquer des pointeurs vers l'avant (laisse un vide)
  • débordement de ligne
  • colonne varchar supprimée sans reconstruction d'index ou DBCC CLEANTABLE
  • tas ou table (le tas n'a pas d'index clusterisé = enregistrements dispersés partout)
  • Niveau d'isolement RCSI (14 octets supplémentaires par ligne)
  • espaces de fin (SET ANSI_PADDING est ON par défaut) dans varchar. Utilisez DATALENGTH pour checl, pas LEN
  • Exécutez sp_spaceused avec @updateusage = 'true'
  • ...

Voir ceci: SQL Server: comment créer une table qui remplit une page de 8 Ko?

De SO:

gbn
la source
L'échantillon de colonne int 2x4 octets n'est pas correct à 100%. Vous aurez un en-tête de ligne de 4 octets (2 octets d'état et 2 octets pour la taille de données de longueur fixe). Ensuite, vous aurez 2x4 octets pour les données. Deux octets pour le nombre de colonnes et un seul octet pour le bitmap nul, ce qui donne une longueur d'enregistrement totale de 15 octets, pas 17.
Mark S. Rasmussen
@Mark S. Rasmussen: Où obtenez-vous "2 octets pour la taille de données de longueur fixe"? MSDN? Et le bitmap null est toujours 3 octets: sqlskills.com/blogs/paul/post/... + msdn.microsoft.com/en-us/library/ms178085%28v=sql.90%29.aspx
gbn
Wow, beaucoup de détails! J'ai pris en compte le champ de longueur des VARCHARs dans mon estimation ci-dessus, mais pas le nombre de colonnes. Cette table n'a pas de champs NULLable (aurait dû le mentionner), lui alloue-t-elle toujours des octets?
Jon of All Trades
La reconstruction des indices affecterait-elle la partie données de l'espace requis? Peut-être que la reconstruction de l'index cluster serait. Les insertions se produisent souvent au milieu, bien que si j'échangeais l'ordre des champs de clustering, cela s'arrêterait. La plupart des autres ne devraient pas s'appliquer dans ce cas, mais c'est une excellente référence pour le cas général. Je vais vérifier vos liens. Bon produit!
Jon of All Trades
1
@gbn Les 2 octets pour la taille de données de longueur fixe font partie de l'en-tête de ligne de 4 octets que vous mentionnez. Il s'agit du pointeur qui pointe vers la fin de la partie de longueur de données fixe / début du nombre de colonnes / bitmap nul. Le bitmap NULL n'est pas toujours de trois octets. Si vous incluez le nombre de colonnes, ce sera un minimum de trois octets, mais peut-être plus - je divise le bitmap et le nombre de colonnes dans ma description. En outre, le bitmap NULL n'est pas toujours présent, bien qu'il le soit dans ce cas.
Mark S. Rasmussen
5

Les types de données ont-ils changé au fil du temps? Les colonnes de longueur variable ont-elles été supprimées? Les index ont-ils été souvent défragmentés mais jamais reconstruits? De nombreuses lignes ont-elles été supprimées ou de nombreuses colonnes de longueur variable ont-elles été mises à jour de manière significative? Une bonne discussion ici .

Aaron Bertrand
la source
Je suis convaincu à 97% que je n'ai pas changé de type de données ou supprimé un champ. Si je l'avais fait, cela aurait été très tôt lorsque la table avait beaucoup moins de lignes. Il n'y a pas de suppressions ni de mises à jour, les données ne sont que jamais ajoutées.
Jon of All Trades
Correction: il y a des suppressions, et pas mal. La table a une croissance nette considérable, donc j'imagine que cet espace serait rapidement réutilisé.
Jon of All Trades
Avec de nombreuses suppressions, les données peuvent être réutilisées ou non. Quelle est la clé de clustering de la table? Les inserts sont-ils au milieu de la table ou à la fin?
mrdenny
La clé en cluster est composée, dans les champs VARCHARet DATETIME2, dans cet ordre. Les inserts seront répartis uniformément pour le premier champ. Pour le deuxième champ, les nouvelles valeurs et seront toujours supérieures à toutes les existantes.
Jon of All Trades