J'ai une table dans une base de données de production d'une taille de 525 Go, dont 383 Go inutilisés:
Je voudrais récupérer une partie de cet espace, mais, avant de jouer avec la base de données de production, je teste certaines stratégies sur une table identique dans une base de données de test avec moins de données. Ce tableau a un problème similaire:
Quelques informations sur la table:
- Le facteur de remplissage est défini sur 0
- Il y a environ 30 colonnes
- L'une des colonnes est un LOB de type image, et il stocke des fichiers dont la taille varie de quelques Ko à plusieurs centaines de Mo
- La table n'a aucun index hypothétique qui lui est associé
Le serveur exécute SQL Server 2017 (RTM-GDR) (KB4505224) - 14.0.2027.2 (X64). La base de données utilise le SIMPLE
modèle de récupération.
Certaines choses que j'ai essayées:
- La reconstruction des index:
ALTER INDEX ALL ON dbo.MyTable REBUILD
. Cela a eu un impact négligeable. - Réorganisant les indices:
ALTER INDEX ALL ON dbo.MyTable REORGANIZE WITH(LOB_COMPACTION = ON)
. Cela a eu un impact négligeable. Copié la colonne LOB dans une autre table, supprimé la colonne, recréé la colonne et recopié les données (comme indiqué dans cet article: Libération de l'espace SQL Server Table SQL Server ). Cela a diminué l'espace inutilisé, mais il semblait simplement le convertir en espace utilisé:
Utilisé l'utilitaire bcp pour exporter la table, la tronquer et la recharger (comme indiqué dans cet article: Comment libérer l'espace inutilisé pour une table ). Cela a également réduit l'espace inutilisé et augmenté l'espace utilisé dans une mesure similaire à l'image ci-dessus.
- Même si ce n'est pas recommandé, j'ai essayé les commandes DBCC SHRINKFILE et DBCC SHRINKDATABASE, mais elles n'ont eu aucun impact sur l'espace inutilisé.
- La course
DBCC CLEANTABLE('myDB', 'dbo.myTable')
n'a pas fait de différence - J'ai essayé tout ce qui précède à la fois tout en conservant les types de données image et texte et après avoir changé les types de données en varbinary (max) et varchar (max).
- J'ai essayé d'importer les données dans une nouvelle table dans une nouvelle base de données, et cela n'a également converti que l'espace inutilisé en espace utilisé. J'ai décrit les détails de cette tentative dans cet article .
Je ne veux pas faire ces tentatives sur la base de données de production si ce sont les résultats auxquels je peux m'attendre, donc:
- Pourquoi l'espace inutilisé est-il simplement converti en espace utilisé après certaines de ces tentatives? J'ai l'impression de ne pas bien comprendre ce qui se passe sous le capot.
- Puis-je faire autre chose pour réduire l'espace inutilisé sans augmenter l'espace utilisé?
EDIT: voici le rapport d'utilisation du disque et le script de la table:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[MyTable](
[Column1] [int] NOT NULL,
[Column2] [int] NOT NULL,
[Column3] [int] NOT NULL,
[Column4] [bit] NOT NULL,
[Column5] [tinyint] NOT NULL,
[Column6] [datetime] NULL,
[Column7] [int] NOT NULL,
[Column8] [varchar](100) NULL,
[Column9] [varchar](256) NULL,
[Column10] [int] NULL,
[Column11] [image] NULL,
[Column12] [text] NULL,
[Column13] [varchar](100) NULL,
[Column14] [varchar](6) NULL,
[Column15] [int] NOT NULL,
[Column16] [bit] NOT NULL,
[Column17] [datetime] NULL,
[Column18] [varchar](50) NULL,
[Column19] [varchar](50) NULL,
[Column20] [varchar](60) NULL,
[Column21] [varchar](20) NULL,
[Column22] [varchar](120) NULL,
[Column23] [varchar](4) NULL,
[Column24] [varchar](75) NULL,
[Column25] [char](1) NULL,
[Column26] [varchar](50) NULL,
[Column27] [varchar](128) NULL,
[Column28] [varchar](50) NULL,
[Column29] [int] NULL,
[Column30] [text] NULL,
CONSTRAINT [PK] PRIMARY KEY CLUSTERED
(
[Column1] ASC,
[Column2] ASC,
[Column3] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[MyTable] ADD CONSTRAINT [DF_Column4] DEFAULT (0) FOR [Column4]
GO
ALTER TABLE [dbo].[MyTable] ADD CONSTRAINT [DF_Column5] DEFAULT (0) FOR [Column5]
GO
ALTER TABLE [dbo].[MyTable] ADD CONSTRAINT [DF_Column15] DEFAULT (0) FOR [Column15]
GO
ALTER TABLE [dbo].[MyTable] ADD CONSTRAINT [DF_Column16] DEFAULT (0) FOR [Column16]
GO
Voici les résultats de l'exécution des commandes dans la réponse de Max Vernon:
╔════════════╦═══════════╦════════════╦═════════════════╦══════════════════════╦════════════════════╗
║ TotalBytes ║ FreeBytes ║ TotalPages ║ TotalEmptyPages ║ PageBytesFreePercent ║ UnusedPagesPercent ║
╠════════════╬═══════════╬════════════╬═════════════════╬══════════════════════╬════════════════════╣
║ 9014280192║ 8653594624║ 1100376║ 997178 ║ 95.998700 ║ 90.621500 ║
╚════════════╩═══════════╩════════════╩═════════════════╩══════════════════════╩════════════════════╝
╔═════════════╦═══════════════════╦════════════════════╗
║ ObjectName ║ ReservedPageCount ║ UsedPageCount ║
╠═════════════╬═══════════════════╬════════════════════╣
║ dbo.MyTable ║ 5109090 ║ 2850245 ║
╚═════════════╩═══════════════════╩════════════════════╝
MISE À JOUR:
J'ai exécuté ce qui suit comme suggéré par Max Vernon:
DBCC UPDATEUSAGE (N'<database_name>', N'<table_name>');
Et voici la sortie:
DBCC UPDATEUSAGE: Usage counts updated for table 'MyTable' (index 'PK_MyTable', partition 1):
USED pages (LOB Data): changed from (568025) to (1019641) pages.
RSVD pages (LOB Data): changed from (1019761) to (1019763) pages.
Cela a mis à jour l'utilisation du disque pour la table:
Et l'utilisation globale du disque:
Ainsi, il semble que le problème était que l'utilisation du disque, telle que suivie par SQL Server, soit complètement désynchronisée avec l'utilisation réelle du disque. Je considérerai ce problème résolu, mais je serais intéressé de savoir pourquoi cela se serait produit en premier lieu!
DBCC UPDATEUSAGE
mis à jour l'espace et le nombre de pages inutilisés. Il semble que l'utilisation du disque et les informations de page signalées par SQL Server soient extrêmement désynchronisées - j'ai mis à jour mon message avec les détails. Je suis curieux de savoir comment cela aurait pu se produire en premier lieu, mais au moins le problème a été trouvé. Merci pour toute votre aide, je l'apprécie vraiment!Vous pourriez rencontrer une fragmentation interne.
Quelle est la fragmentation des pages pour ce tableau?
Et la fragmentation des lignes en ligne est-elle différente des pages hors ligne?
Vous dites que vous avez des fichiers de quelques Ko.
SQL Server stocke tout dans des pages de 8060 octets. Cela signifie que si vous avez une ligne (ou des données hors ligne) de 4040 octets et que la suivante est similaire, elle ne peut pas tenir toutes les deux sur la même page et vous gaspillerez la moitié de votre espace. Essayez de changer la taille de votre ligne en stockant des colonnes de longueur variable (commencez par l'image par exemple) dans un autre tableau.
la source
La base de données est-elle en mode de récupération complète? Si c'est le cas, lorsque vous effectuez une réduction, il enregistre toutes les modifications et ne la réduira pas comme vous l'attendez. En fonction de vos heures d'ouverture, vous pouvez effectuer une sauvegarde, passer en mode de récupération de livraison groupée, puis exécuter la réduction sur le fichier de données. Après cela, vous souhaitez exécuter des scripts d'index pour réparer / reconstruire et revenir à une récupération complète. C'est ce que j'essaierais de toute façon, mais encore une fois, cela dépend de vos heures d'ouverture pour tout cela.
la source
La seule fois où je n'ai pas pu réduire une base de données et récupérer de l'espace, c'est parce que vous ne pouvez pas réduire une base de données au-delà de la taille initiale de la base de données lors de sa création. Ainsi, par exemple, si votre base de données est une copie de la base de données de production et que vous avez d'abord créé la base de données à 525 Go, le serveur SQL ne vous permettra pas de réduire la taille en dessous de 525 Go, peu importe la quantité de données que vous supprimez de la base de données. Mais si la base de données a été créée en dessous de 383 Go et est ensuite passée à 525 Go, vous ne devriez pas avoir de problème à récupérer l'espace. J'ai longtemps pensé que c'était une restriction stupide et arbitraire de Microsoft.
Rétrécir la base de données uniquement jusqu'à sa taille initiale définie après la création de la base de données
la source
J'ai rencontré ce problème auparavant sur les boîtes de production, ce que vous devez faire est de reconstruire les tables et les index pour chaque table (dans cet ordre).
Voici la requête que j'utilise pour contrôler les tables. Il vous aidera à déterminer quelles tables doivent être reconstruites et à créer les requêtes SQL que vous devez exécuter. Cette requête est limitée à ceux qui ont plus de 1 Mo d'espace inutilisé et un ratio inutilisé de 5%, de sorte que vous ne reconstruisez que ce sur quoi vous devez vraiment vous concentrer:
la source