J'ai créé le tableau suivant:
CREATE TABLE dbo.TestStructure
(
id INT NOT NULL,
filler1 CHAR(36) NOT NULL,
filler2 CHAR(216) NOT NULL
);
puis créé un index clusterisé:
CREATE CLUSTERED INDEX idx_cl_id
ON dbo.TestStructure(id);
Ensuite, je l'ai rempli avec 30 lignes, chaque taille est de 256 octets (basée sur la déclaration de la table):
DECLARE @i AS int = 0;
WHILE @i < 30
BEGIN
SET @i = @i + 1;
INSERT INTO dbo.TestStructure (id, filler1, filler2)
VALUES (@i, 'a', 'b');
END;
Maintenant basé sur les informations que j'ai lues dans le livre "Kit de formation (examen 70-461): Interrogation de Microsoft SQL Server 2012 (Itzik Ben-Gan)":
SQL Server organise en interne les données dans un fichier de données en pages. Une page est une unité de 8 Ko et appartient à un seul objet; par exemple, vers une table ou un index. Une page est la plus petite unité de lecture et d'écriture. Les pages sont en outre organisées en étendues. Une étendue se compose de huit pages consécutives. Les pages d'une étendue peuvent appartenir à un seul objet ou à plusieurs objets. Si les pages appartiennent à plusieurs objets, l'extension est appelée extension mixte; si les pages appartiennent à un seul objet, alors l'étendue est appelée une étendue uniforme. SQL Server stocke les huit premières pages d'un objet dans des extensions mixtes. Lorsqu'un objet dépasse huit pages, SQL Server alloue des extensions uniformes supplémentaires pour cet objet. Avec cette organisation, les petits objets perdent moins d'espace et les gros objets sont moins fragmentés.
J'ai donc ici la première page d'étendue mixte de 8 Ko, remplie de 7680 octets (j'ai inséré 30 fois la ligne de taille de 256 octets, donc 30 * 256 = 7680), pour vérifier la taille, j'ai exécuté la vérification de la taille proc - elle renvoie le résultat suivant
index_type_desc: CLUSTERED INDEX
index_depth: 1
index_level: 0
page_count: 1
record_count: 30
avg_page_space_used_in_percent: 98.1961947121324
name : TestStructure
rows : 30
reserved : 16 KB
data : 8 KB
index_size : 8 KB
unused : 0 KB
Donc 16 Ko sont réservés pour la table, la première page de 8 Ko est pour la page Root IAM, la seconde est pour la page de stockage de données feuille qui est de 8 Ko avec une occupation de ~ 7,5 Ko, maintenant lorsque j'insère une nouvelle ligne avec 256 octets:
INSERT INTO dbo.TestStructure (id, filler1, filler2)
VALUES (1, 'a', 'b');
il n'est pas stocké dans la même page bien qu'il ait un espace de 256 octets (7680 b + 256 = 7936 qui est encore plus petit que 8 Ko), une nouvelle page de données est créée, mais cette nouvelle ligne pourrait tenir sur la même ancienne page , pourquoi SQL Server crée-t-il une nouvelle page alors qu'il peut économiser de l'espace et du temps de recherche en l'insérant dans la page existante?
Remarque: la même chose se produit dans l'index de tas.
la source
S'il est vrai que SQL Server utilise des pages de données de 8k (8192 octets) pour stocker 1 ou plusieurs lignes, chaque page de données a une surcharge (96 octets) et chaque ligne a une surcharge (au moins 9 octets). Les 8192 octets ne sont pas uniquement des données.
Pour un examen plus détaillé de la façon dont cela fonctionne, veuillez consulter ma réponse à la question DBA.SE suivante:
SUM des DATALENGTH ne correspondant pas à la taille de la table de sys.allocation_units
En utilisant les informations de cette réponse liée, nous pouvons obtenir une image plus claire de la taille réelle de la ligne:
L'utilisation
DBCC PAGE
confirme mon calcul en affichant:Record Size 263
(pourtempdb
) etRecord Size 277
(pour une base de données définie surALLOW_SNAPSHOT_ISOLATION ON
).Maintenant, avec 30 lignes, c'est:
SANS info de version
30 * 263 nous donnerait 7890 octets. Ajoutez ensuite les 96 octets d'en-tête de page pour 7986 octets utilisés. Enfin, ajoutez les 60 octets (2 par ligne) de la baie de logements pour un total de 8046 octets utilisés sur la page et 146 restants. L'utilisation
DBCC PAGE
confirme mon calcul en montrant:m_slotCnt 30
(ie nombre de lignes)m_freeCnt 146
(c'est-à-dire le nombre d'octets restants sur la page)m_freeData 7986
(c.-à-d. données + en-tête de page - 7890 + 96 - le tableau des emplacements n'est pas pris en compte dans le calcul des octets "utilisés")AVEC les informations de version
30 * 277 octets pour un total de 8310 octets. Mais 8310 est supérieur à 8192, et cela ne tenait même pas compte de l'en-tête de page de 96 octets ni du tableau d'emplacements de 2 octets par ligne (30 * 2 = 60 octets), ce qui ne devrait nous donner que 8036 octets utilisables pour les lignes.
MAIS, qu'en est-il de 29 lignes? Cela nous donnerait 8033 octets de données (29 * 277) + 96 octets pour l'en-tête de page + 58 octets pour la baie de slots (29 * 2) soit 8187 octets. Et cela laisserait la page avec 5 octets restants (8192 - 8187; inutilisable, bien sûr). L'utilisation
DBCC PAGE
confirme mon calcul en montrant:m_slotCnt 29
(ie nombre de lignes)m_freeCnt 5
(c'est-à-dire le nombre d'octets restants sur la page)m_freeData 8129
(c'est-à-dire données + en-tête de page - 8033 + 96 - le tableau des emplacements n'est pas pris en compte dans le calcul des octets "utilisés")Concernant les tas
Les tas remplissent les pages de données légèrement différemment. Ils conservent une estimation très approximative de la quantité d'espace restant sur la page. Lorsque l'on regarde à la sortie de DBCC, regardez la ligne pour:
PAGE HEADER: Allocation Status PFS (1:1)
. Vous verrez leVALUE
montrant quelque chose le long des lignes de0x60 MIXED_EXT ALLOCATED 0_PCT_FULL
(quand j'ai regardé la table Clustered) ou0x64 MIXED_EXT ALLOCATED 100_PCT_FULL
en regardant la table Heap. Ceci est évalué par transaction, donc faire des insertions individuelles telles que le test effectué ici pourrait afficher des résultats différents entre les tables en cluster et les tables de tas. Cependant, effectuer une seule opération DML pour les 30 lignes remplira le tas comme prévu.Cependant, aucun de ces détails concernant les tas n'affecte directement ce test particulier car les deux versions du tableau tiennent sur 30 lignes avec seulement 146 octets restants. Ce n'est pas assez d'espace pour une autre ligne, indépendamment de Clustered ou Heap.
N'oubliez pas que ce test est assez simple. Le calcul de la taille réelle d'une ligne peut devenir très compliqué en fonction de divers facteurs, tels que
SPARSE
:, compression des données, données LOB, etc.Pour voir les détails de la page de données, utilisez la requête suivante:
** La valeur "version info" de 14 octets sera présente si votre base de données est définie sur
ALLOW_SNAPSHOT_ISOLATION ON
ouREAD_COMMITTED_SNAPSHOT ON
.la source
tempdb
? J'ai pu reproduire les chiffres exacts fournis par OP.ALLOW_SNAPSHOT_ISOLATION
. J'ai aussi juste essayétempdb
et vu que les "informations sur la version" ne sont pas là, donc 30 rangées conviennent. Je mettrai à jour pour ajouter les nouvelles informations.La structure réelle de la page de données est assez complexe. Bien qu'il soit généralement indiqué que 8060 octets par page sont disponibles pour les données utilisateur, il existe une surcharge supplémentaire non comptée nulle part qui entraîne ce comportement.
Cependant, vous avez peut-être remarqué que SQL Server vous donne en fait un indice que la 31e ligne ne rentrera pas dans la page. Pour que la ligne suivante tienne sur la même page, la
avg_page_space_used_in_percent
valeur doit être inférieure à 100% - (100/31) = 96,774194, et dans votre cas, c'est bien au-dessus.PS Je crois avoir vu une explication détaillée, jusqu'à l'octet de la structure de la page de données dans l'un des livres "SQL Server Internals" de Kalen Delaney, mais c'était il y a presque 10 ans, alors veuillez m'excuser de ne pas me souvenir de plus de détails. En outre, la structure des pages a tendance à changer d'une version à l'autre.
la source