web-dev-qa-db-fra.com

La table a 14 Go d'espace inutilisé - Comment réduire la taille de la table

J'utilise le script suivant pour collecter des données à partir de ma base de données qui est SQL Server sur Azure.

-- Script to run against database to gather metrics

CREATE TABLE #SpaceUsed (name sysname,rows bigint,reserved sysname,data sysname,index_size sysname,unused sysname)

DECLARE @Counter int 
DECLARE @Max int 
DECLARE @Table sysname

SELECT  name, IDENTITY(int,1,1) ROWID 
INTO       #TableCollection 
FROM    sysobjects 
WHERE xtype = 'U' 
ORDER BY lower(name)

SET @Counter = 1 
SET @Max = (SELECT Max(ROWID) FROM #TableCollection)

WHILE (@Counter <= @Max) 
    BEGIN 
        SET @Table = (SELECT name FROM #TableCollection WHERE ROWID = @Counter) 
        INSERT INTO #SpaceUsed 
        EXECUTE sp_spaceused @Table 
        SET @Counter = @Counter + 1 
    END

SELECT * FROM #SpaceUsed

DROP TABLE #TableCollection 
DROP TABLE #SpaceUsed

Une de mes tables est 53 GB in size, avec 38 GB in data, 14 GB in unused. Les index sont 11 MB in size. Comment puis-je récupérer ce 14 GB that is unused? Comme c'est dans Azure, l'espace supplémentaire coûte de l'argent.

Après avoir lu divers articles, j'ai essayé ce qui suit pour réduire la taille du tableau.

DBCC DBREINDEX ('myTableName', ' ')
DBCC SHRINKDATABASE (myDatabaseName, 10);

Cependant, une fois ces étapes terminées, la taille de la table reste inchangée. Comment puis-je réduire la taille de la table et de la base de données afin de récupérer l'espace inutilisé?

MISE À JOUR: Il y a 24 colonnes dans le tableau avec 9 des champs étant varchar de grande longueur (> 4000) ou varchar (MAX). 8 autres colonnes sont des types à identifiant unique.

4
webworm

Qu'il s'agisse d'un segment de mémoire ou d'un index clusterisé, vous devriez constater que vous pourrez récupérer de l'espace dans une table en reconstruisant:

ALTER INDEX ALL ON dbo.myTableName REBUILD;

Notez que la récupération d'espace dans une table et la réduction d'une base de données sont deux choses complètement distinctes. La réduction d'une base de données devrait être une chose exceptionnelle - ne réduisez pas une base de données uniquement pour libérer de l'espace disque, car dans la plupart des cas, la base de données devra simplement s'agrandir et réutiliser cet espace de toute façon.

Quant à déterminer comment la table est distribuée, sp_spaceused est assez inutile, car il ne vous donne qu'un seul élément de ligne avec toutes les données. Tout d'abord, vérifiez si votre table est partitionnée. Vous pouvez le voir rapidement en utilisant:

SELECT COUNT(*)
  FROM sys.partitions
  WHERE partition_number > 1
  AND object_id = OBJECT_ID(N'dbo.myTableName');

Ensuite, voyez comment les données sont réparties sur vos sept index. Si l'un d'entre eux est beaucoup plus grand qu'il ne devrait l'être, vous pouvez essayer de supprimer/recréer (y compris potentiellement supprimer, par exemple, votre colonne XML de la liste INCLUDE), car il peut y avoir des cas où l'espace LOB pour les lignes que vous avez supprimées ou définies sur NULL ne sont pas entièrement récupérées, même après une reconstruction.

 ;WITH x AS
  ( 
    SELECT i.name, type = pa.allocation_unit_type_desc, kb = COUNT(*) * 8
    FROM sys.indexes AS i
    CROSS APPLY sys.dm_db_database_page_allocations(DB_ID(), i.object_id, i.index_id, 
      NULL, 'LIMITED') AS pa
    WHERE i.[object_id] = OBJECT_ID(N'dbo.myTableName')
    GROUP BY GROUPING SETS((), (i.name, pa.allocation_unit_type_desc))
  )
  SELECT name, type, kb, 
    [%] = CONVERT(decimal(5,2), kb*100.0/(SELECT kb FROM x WHERE name IS NULL))
    FROM x
    WHERE name IS NOT NULL
    ORDER BY name, type;

Si vous souhaitez voir les tailles de ligne individuelles dans la table de base, vous pouvez faire:

SELECT TOP (100) key, rowsize = DATALENGTH(col1) + DATALENGTH(col2) + ...
  FROM dbo.myTableName
  ORDER BY rowsize DESC;

Si vous en avez qui sont assez volumineuses, vous pouvez vous assurer que vous avez besoin de ces données (sinon, définissez-les sur NULL, car cela peut être ce qui contribue à votre taille.

8
Aaron Bertrand

Vous devez vérifier le facteur de remplissage sur vos tables et index, pour vous assurer que vous ne réservez pas explicitement beaucoup d'espace sur vos pages. Exécutez la commande suivante:

SELECT t.name as TableName
      ,COALESCE(i.name,'<no index>') as IndexName
      ,i.type_desc
      ,i.Fill_Factor
  FROM sys.tables t INNER JOIN sys.indexes i ON t.object_id = i.object_id
;

Le facteur de remplissage indique au moteur SQL la quantité d'espace libre à laisser sur chaque page qu'il crée lorsqu'il remplit une table ou un index. Une fois les pages remplies, SQL ajoutera des informations à la page jusqu'à ce qu'elle soit trop pleine, puis créera une nouvelle page et déplacera une quantité appropriée (généralement environ la moitié) des données vers la nouvelle page.

Si l'un de vos index a un facteur de remplissage faible (inférieur à 75, mais supérieur à 0 (0 signifie en fait remplir à 100%, essentiellement)), il est possible que cela explique une grande quantité d'espace inutilisé dans votre base de données.

Si vous souhaitez modifier le facteur de remplissage de vos index à (par exemple) 90%, vous pouvez utiliser une légère variation sur le ALTER INDEX commande de la réponse d'Aaron Bertrand:

ALTER INDEX ALL ON dbo.myTableName REBUILD WITH FILLFACTOR = 90;
3
RDFozz