Présentation
Il est important pour un administrateur de base de données de savoir quand il n'y a plus d'espace sur un disque. Ainsi, il est préférable d'automatiser le processus afin qu'ils ne le fassent pas manuellement sur chaque serveur.
Dans cet article, je vais décrire comment implémenter la collecte quotidienne automatique de données sur les lecteurs logiques et les fichiers de base de données.
Solution
Algorithme :
1. Créer des tables de stockage de données :
1.1. pour les fichiers de base de données :
USE [DATABASE_NAME]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [srv].[DBFile](
[DBFile_GUID] [uniqueidentifier] ROWGUIDCOL NOT NULL,
[Server] [nvarchar](255) NOT NULL,
[Name] [nvarchar](255) NOT NULL,
[Drive] [nvarchar](10) NOT NULL,
[Physical_Name] [nvarchar](255) NOT NULL,
[Ext] [nvarchar](255) NOT NULL,
[Growth] [int] NOT NULL,
[IsPercentGrowth] [int] NOT NULL,
[DB_ID] [int] NOT NULL,
[DB_Name] [nvarchar](255) NOT NULL,
[SizeMb] [float] NOT NULL,
[DiffSizeMb] [float] NOT NULL,
[InsertUTCDate] [datetime] NOT NULL,
[UpdateUTCdate] [datetime] NOT NULL,
[File_ID] [int] NOT NULL,
CONSTRAINT [PK_DBFile] PRIMARY KEY CLUSTERED
(
[DBFile_GUID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [srv].[DBFile] ADD CONSTRAINT [DF_DBFile_DBFile_GUID]
DEFAULT (newid()) FOR [DBFile_GUID]
GO
ALTER TABLE [srv].[DBFile] ADD CONSTRAINT [DF_DBFile_InsertUTCDate]
DEFAULT (getutcdate()) FOR [InsertUTCDate]
GO
ALTER TABLE [srv].[DBFile] ADD CONSTRAINT [DF_DBFile_UpdateUTCdate]
DEFAULT (getutcdate()) FOR [UpdateUTCdate]
GO 1.2. pour les lecteurs logiques :
USE [DATABASE_NAME]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [srv].[Drivers](
[Driver_GUID] [uniqueidentifier] ROWGUIDCOL NOT NULL,
[Server] [nvarchar](255) NOT NULL,
[Name] [nvarchar](8) NOT NULL,
[TotalSpace] [float] NOT NULL,
[FreeSpace] [float] NOT NULL,
[DiffFreeSpace] [float] NOT NULL,
[InsertUTCDate] [datetime] NOT NULL,
[UpdateUTCdate] [datetime] NOT NULL,
CONSTRAINT [PK_Drivers] PRIMARY KEY CLUSTERED
(
[Driver_GUID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [srv].[Drivers] ADD CONSTRAINT [DF_Drivers_Driver_GUID]
DEFAULT (newid()) FOR [Driver_GUID]
GO
ALTER TABLE [srv].[Drivers] ADD CONSTRAINT [DF_Drivers_Server]
DEFAULT (@@servername) FOR [Server]
GO
ALTER TABLE [srv].[Drivers] ADD CONSTRAINT [DF_Drivers_TotalSpace]
DEFAULT ((0)) FOR [TotalSpace]
GO
ALTER TABLE [srv].[Drivers] ADD CONSTRAINT [DF_Drivers_FreeSpace]
DEFAULT ((0)) FOR [FreeSpace]
GO
ALTER TABLE [srv].[Drivers] ADD CONSTRAINT [DF_Drivers_DiffFreeSpace]
DEFAULT ((0)) FOR [DiffFreeSpace]
GO
ALTER TABLE [srv].[Drivers] ADD CONSTRAINT [DF_Drivers_InsertUTCDate]
DEFAULT (getutcdate()) FOR [InsertUTCDate]
GO
ALTER TABLE [srv].[Drivers] ADD CONSTRAINT [DF_Drivers_UpdateUTCdate]
DEFAULT (getutcdate()) FOR [UpdateUTCdate]
GO
De plus, vous devez remplir à l'avance un tableau avec les lecteurs logiques de la manière suivante :
Nom du serveur - étiquette de volume
2. créez une vue nécessaire pour la collecte de données sur les fichiers de base de données :
USE [DATABASE_NAME]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE view [inf].[ServerDBFileInfo] as
SELECT @@Servername AS Server ,
File_id ,--file_id in a database. Its main value always equals 1
Type_desc ,--description of a file type
Name as [FileName] ,--logic file name in a database
LEFT(Physical_Name, 1) AS Drive ,--volume label where a database file is located
Physical_Name ,--a full name of a file in the operating system
RIGHT(physical_name, 3) AS Ext ,--file extension
Size as CountPage, --current file size in pages of 8 Kb
round((cast(Size*8 as float))/1024,3) as SizeMb, --file size in Mb
Growth, --growth
is_percent_growth, --growth in %
database_id,
DB_Name(database_id) as [DB_Name]
FROM sys.master_files--database_files
GO Ici, la vue système sys.master_files est utilisée.
3. Créez une procédure stockée qui renvoie des informations sur un lecteur logique :
USE [DATABASE_NAME]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
create procedure [srv].[sp_DriveSpace]
@DrivePath varchar(1024) --device (it is possible to set a volume label 'C:')
, @TotalSpace float output --total volume in bytes
, @FreeSpace float output --free disk space in bytes
as
begin
DECLARE @fso int
, @Drive int
, @DriveName varchar(255)
, @Folder int
, @Drives int
, @source varchar(255)
, @desc varchar(255)
, @ret int
, @Object int
-- Create an object of a file system
exec @ret = sp_OACreate 'Scripting.FileSystemObject', @fso output
set @Object = @fso
if @ret != 0
goto ErrorInfo
-- Get a folder on the specified path
exec @ret = sp_OAmethod @fso, 'GetFolder', @Folder output, @DrivePath
set @Object = @fso
if @ret != 0
goto ErrorInfo
-- Get a device
exec @ret = sp_OAmethod @Folder, 'Drive', @Drive output
set @Object = @Folder
if @ret != 0
goto ErrorInfo
-- Determine the whole device storage space
exec @ret = sp_OAGetProperty @Drive, 'TotalSize', @TotalSpace output
set @Object = @Drive
if @ret != 0
goto ErrorInfo
-- Determine a free space on a disk
exec @ret = sp_OAGetProperty @Drive, 'AvailableSpace', @FreeSpace output
set @Object = @Drive
if @ret != 0
goto ErrorInfo
DestroyObjects:
if @Folder is not null
exec sp_OADestroy @Folder
if @Drive is not null
exec sp_OADestroy @Drive
if @fso is not null
exec sp_OADestroy @fso
return (@ret)
ErrorInfo:
exec sp_OAGetErrorInfo @Object, @source output, @desc output
print 'Source error: ' + isnull( @source, 'n/a' ) + char(13) + 'Description: ' + isnull( @desc, 'n/a' )
goto DestroyObjects;
end
GO Pour obtenir des informations détaillées sur cette procédure, reportez-vous à l'article suivant :Espace disque dans T-SQL.
4. Créez une procédure stockée pour la collecte de données :
4.1. pour les fichiers de base de données :
USE [DATABASE_NAME]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [srv].[MergeDBFileInfo]
AS
BEGIN
SET NOCOUNT ON;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
;merge [srv].[DBFile] as f
using [inf].[ServerDBFileInfo] as ff
on f.File_ID=ff.File_ID and f.DB_ID=ff.[database_id] and f.[Server]=ff.[Server]
when matched then
update set UpdateUTcDate = getUTCDate()
,[Name] = ff.[FileName]
,[Drive] = ff.[Drive]
,[Physical_Name] = ff.[Physical_Name]
,[Ext] = ff.[Ext]
,[Growth] = ff.[Growth]
,[IsPercentGrowth] = ff.[is_percent_growth]
,[SizeMb] = ff.[SizeMb]
,[DiffSizeMb] = round(ff.[SizeMb]-f.[SizeMb],3)
when not matched by target then
insert (
[Server]
,[Name]
,[Drive]
,[Physical_Name]
,[Ext]
,[Growth]
,[IsPercentGrowth]
,[DB_ID]
,[DB_Name]
,[SizeMb]
,[File_ID]
,[DiffSizeMb]
)
values (
ff.[Server]
,ff.[FileName]
,ff.[Drive]
,ff.[Physical_Name]
,ff.[Ext]
,ff.[Growth]
,ff.[is_percent_growth]
,ff.[database_id]
,ff.[DB_Name]
,ff.[SizeMb]
,ff.[File_id]
,0
)
when not matched by source and f.[Server]example@sqldat.com@SERVERNAME then delete;
END
GO 4.2. pour les lecteurs logiques :
USE [DATABASE_NAME]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [srv].[MergeDriverInfo]
AS
BEGIN
SET NOCOUNT ON;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
declare @Drivers table (
[Server] nvarchar(255),
Name nvarchar(8),
TotalSpace float,
FreeSpace float,
DiffFreeSpace float NULL
);
insert into @Drivers (
[Server],
Name,
TotalSpace,
FreeSpace
)
select [Server],
Name,
TotalSpace,
FreeSpace
from srv.Drivers
where [Server]example@sqldat.com@SERVERNAME;
declare @TotalSpace float;
declare @FreeSpace float;
declare @DrivePath nvarchar(8);
while(exists(select top(1) 1 from @Drivers where DiffFreeSpace is null))
begin
select top(1)
@DrivePath=Name
from @Drivers
where DiffFreeSpace is null;
exec srv.sp_DriveSpace @DrivePath = @DrivePath
, @TotalSpace = @TotalSpace out
, @FreeSpace = @FreeSpace out;
update @Drivers
set example@sqldat.com
,example@sqldat.com
,DiffFreeSpace=case when FreeSpace>0 then round(example@sqldat.com,3) else 0 end
where example@sqldat.com;
end
;merge [srv].[Drivers] as d
using @Drivers as dd
on d.Name=dd.Name and d.[Server]=dd.[Server]
when matched then
update set UpdateUTcDate = getUTCDate()
,[TotalSpace] = dd.[TotalSpace]
,[FreeSpace] = dd.[FreeSpace]
,[DiffFreeSpace]= dd.[DiffFreeSpace];
END
GO 5. Créez des vues pour la sortie des données :
5.1. pour les fichiers de base de données :
USE [DATABASE_NAME]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
create view [srv].[vDBFiles] as
SELECT [DBFile_GUID]
,[Server]
,[Name]
,[Drive]
,[Physical_Name]
,[Ext]
,[Growth]
,[IsPercentGrowth]
,[DB_ID]
,[File_ID]
,[DB_Name]
,[SizeMb]
,[DiffSizeMb]
,round([SizeMb]/1024,3) as [SizeGb]
,round([DiffSizeMb]/1024,3) as [DiffSizeGb]
,round([SizeMb]/1024/1024,3) as [SizeTb]
,round([DiffSizeMb]/1024/1024,3) as [DiffSizeTb]
,round([DiffSizeMb]/([SizeMb]/100), 3) as [DiffSizePercent]
,[InsertUTCDate]
,[UpdateUTCdate]
FROM [srv].[DBFile];
GO 5.2. pour les disques logiques :
USE [DATABASE_NAME]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
create view [srv].[vDrivers] as
select
[Driver_GUID]
,[Server]
,[Name]
,[TotalSpace] as [TotalSpaceByte]
,[FreeSpace] as [FreeSpaceByte]
,[DiffFreeSpace] as [DiffFreeSpaceByte]
,round([TotalSpace]/1024, 3) as [TotalSpaceKb]
,round([FreeSpace]/1024, 3) as [FreeSpaceKb]
,round([DiffFreeSpace]/1024, 3) as [DiffFreeSpaceKb]
,round([TotalSpace]/1024/1024, 3) as [TotalSpaceMb]
,round([FreeSpace]/1024/1024, 3) as [FreeSpaceMb]
,round([DiffFreeSpace]/1024/1024, 3) as [DiffFreeSpaceMb]
,round([TotalSpace]/1024/1024/1024, 3) as [TotalSpaceGb]
,round([FreeSpace]/1024/1024/1024, 3) as [FreeSpaceGb]
,round([DiffFreeSpace]/1024/1024/1024, 3) as [DiffFreeSpaceGb]
,round([TotalSpace]/1024/1024/1024/1024, 3) as [TotalSpaceTb]
,round([FreeSpace]/1024/1024/1024/1024, 3) as [FreeSpaceTb]
,round([DiffFreeSpace]/1024/1024/1024/1024, 3) as [DiffFreeSpaceTb]
,round([FreeSpace]/([TotalSpace]/100), 3) as [FreeSpacePercent]
,round([DiffFreeSpace]/([TotalSpace]/100), 3) as [DiffFreeSpacePercent]
,[InsertUTCDate]
,[UpdateUTCdate]
FROM [srv].[Drivers]
GO 6. Créez une tâche dans l'Agent SQL Server et exécutez-la une fois par jour :
USE [DATABASE_NAME]; GO exec srv.MergeDBFileInfo; exec srv.MergeDriverInfo;
7. Collectez toutes les sorties de données des serveurs. Vous pouvez le faire en utilisant l'Agent SQL Server, par exemple.
8. Créez une procédure stockée pour générer un rapport et l'envoyer aux administrateurs. Puisqu'il est possible de l'implémenter de différentes manières, je vais le considérer sur cet exemple particulier :
USE [DATABASE_NAME]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [srv].[GetHTMLTableShortInfoDrivers]
@body nvarchar(max) OUTPUT
AS
BEGIN
SET NOCOUNT ON;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
declare @tbl table (
Driver_GUID uniqueidentifier
,[Name] nvarchar(255)
,[TotalSpaceGb] float
,[FreeSpaceGb] float
,[DiffFreeSpaceMb] float
,[FreeSpacePercent] float
,[DiffFreeSpacePercent] float
,UpdateUTCDate datetime
,[Server] nvarchar(255)
,ID int identity(1,1)
);
declare
@Driver_GUID uniqueidentifier
,@Name nvarchar(255)
,@TotalSpaceGb float
,@FreeSpaceGb float
,@DiffFreeSpaceMb float
,@FreeSpacePercent float
,@DiffFreeSpacePercent float
,@UpdateUTCDate datetime
,@Server nvarchar(255)
,@ID int;
insert into @tbl(
Driver_GUID
,[Name]
,[TotalSpaceGb]
,[FreeSpaceGb]
,[DiffFreeSpaceMb]
,[FreeSpacePercent]
,[DiffFreeSpacePercent]
,UpdateUTCDate
,[Server]
)
select Driver_GUID
,[Name]
,[TotalSpaceGb]
,[FreeSpaceGb]
,[DiffFreeSpaceMb]
,[FreeSpacePercent]
,[DiffFreeSpacePercent]
,UpdateUTCDate
,[Server]
from srv.vDrivers
where [DiffFreeSpacePercent]<=-5
or [FreeSpacePercent]<=15
order by [Server] asc, [Name] asc;
if(exists(select top(1) 1 from @tbl))
begin
set @body='When analyzing I have got the data storage devices that either have free disk space less than 15%, or free space decreases over 5% a day:<br><br>'+'<TABLE BORDER=5>';
set @example@sqldat.com+'<TR>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+'№ p/p';
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+'GUID';
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+'SEVER';
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+'TOM';
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+'VOLUME, GB.';
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+'FREE, GB.';
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+'FREE SPACE CHANGE, MB.';
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+'FREE, %';
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+'FREE SPACE CHANGE, %';
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+'UTC DETECTION TIME';
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'</TR>';
while((select top 1 1 from @tbl)>0)
begin
set @example@sqldat.com+'<TR>';
select top 1
@Driver_GUID = Driver_GUID
,@Name = Name
,@TotalSpaceGb = TotalSpaceGb
,@FreeSpaceGb = FreeSpaceGb
,@DiffFreeSpaceMb = DiffFreeSpaceMb
,@FreeSpacePercent = FreeSpacePercent
,@DiffFreeSpacePercent = DiffFreeSpacePercent
,@UpdateUTCDate = UpdateUTCDate
,@Server = [Server]
,@ID = [ID]
from @tbl;
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+cast(@ID as nvarchar(max));
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+cast(@Driver_GUID as nvarchar(255));
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+coalesce(@Server,'');
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+coalesce(@Name,'');
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+cast(@TotalSpaceGb as nvarchar(255));
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+cast(@FreeSpaceGb as nvarchar(255));
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+cast(@DiffFreeSpaceMb as nvarchar(255));
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+cast(@FreeSpacePercent as nvarchar(255));
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+cast(@DiffFreeSpacePercent as nvarchar(255));
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+rep.GetDateFormat(@UpdateUTCDate, default)+' '+rep.GetTimeFormat(@UpdateUTCDate, default);
set @example@sqldat.com+'</TD>';
delete from @tbl
where example@sqldat.com;
set @example@sqldat.com+'</TR>';
end
set @example@sqldat.com+'</TABLE>';
set @example@sqldat.com+'<br><br>';
To get the detailed information, refer to the view SRV.srv.vDrivers<br><br>
To view the information on database files, refer to the view DATABASE_NAME.srv.vDBFiles';
end
END
GO Cette procédure stockée génère un rapport HTML sur les lecteurs logiques dont l'espace disque libre est inférieur à 15 % ou dont l'espace libre diminue de plus de 5 % par jour. Ce dernier montre une étrange activité d'enregistrements qui signifie que quelqu'un stocke très souvent trop d'informations sur ce disque. Cela peut arriver pour les raisons suivantes :
- C'est le moment d'étendre un disque ;
- Il est nécessaire de supprimer les fichiers inutilisés sur un lecteur logique ;
- Effacer et réduire les fichiers journaux, ainsi que les fichiers d'informations et autres tables.
Solution
Dans cet article, j'ai analysé un exemple de mise en œuvre d'un système de collecte automatique quotidienne de données sur les lecteurs locaux et les fichiers de base de données. Cette information permet de savoir à l'avance quel disque a le moins d'espace libre, ainsi que quels fichiers de base de données augmentent considérablement. Cela permet d'éviter un cas où il n'y a pas d'espace sur un disque et de trouver une raison pour laquelle un processus prend beaucoup d'espace sur un disque.
Lire aussi :
Collecte automatique des données des modifications du schéma de base de données dans MS SQL Server
Collecte automatique de données sur les tâches terminées dans MS SQL Server