Einführung
Für einen Datenbankadministrator ist es wichtig zu wissen, wann auf einer Festplatte kein Platz mehr vorhanden ist. Daher ist es besser, den Prozess zu automatisieren, damit sie ihn nicht manuell auf jedem Server durchführen müssen.
In diesem Artikel werde ich beschreiben, wie man eine automatische tägliche Datenerfassung über logische Laufwerke und Datenbankdateien implementiert.
Lösung
Algorithmus:
1. Erstellen Sie Tabellen zur Datenspeicherung:
1.1. für Datenbankdateien:
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. für logische Laufwerke:
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
Außerdem müssen Sie vorab eine Tabelle mit logischen Laufwerken wie folgt ausfüllen:
Servername – Datenträgerbezeichnung
2. Erstellen Sie eine notwendige Ansicht für die Datenerfassung über Datenbankdateien:
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
Hier wird die Systemansicht sys.master_files verwendet.
3. Erstellen Sie eine gespeicherte Prozedur, die Informationen zu einem logischen Laufwerk zurückgibt:
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
Ausführliche Informationen zu diesem Verfahren finden Sie im folgenden Artikel:Speicherplatz in T-SQL.
4. Erstellen Sie eine gespeicherte Prozedur für die Datenerfassung:
4.1. für Datenbankdateien:
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][email protected]@SERVERNAME then delete; END GO
4.2. für logische Laufwerke:
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][email protected]@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 [email protected] ,[email protected] ,DiffFreeSpace=case when FreeSpace>0 then round([email protected],3) else 0 end where [email protected]; 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. Erstellen Sie Ansichten für die Datenausgabe:
5.1. für Datenbankdateien:
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. für logische Laufwerke:
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. Erstellen Sie eine Aufgabe im SQL Server Agent und führen Sie sie einmal täglich aus:
USE [DATABASE_NAME]; GO exec srv.MergeDBFileInfo; exec srv.MergeDriverInfo;
7. Sammeln Sie alle Datenausgaben von Servern. Sie können dies beispielsweise mit dem SQL Server Agent tun.
8. Erstellen Sie eine gespeicherte Prozedur zum Generieren eines Berichts und zum Senden an Administratoren. Da es auf verschiedene Arten implementiert werden kann, werde ich es an diesem speziellen Beispiel betrachten:
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 @[email protected]+'<TR>'; set @[email protected]+'<TD>'; set @[email protected]+'№ p/p'; set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+'GUID'; set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+'SEVER'; set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+'TOM'; set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+'VOLUME, GB.'; set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+'FREE, GB.'; set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+'FREE SPACE CHANGE, MB.'; set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+'FREE, %'; set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+'FREE SPACE CHANGE, %'; set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+'UTC DETECTION TIME'; set @[email protected]+'</TD>'; set @[email protected]+'</TR>'; while((select top 1 1 from @tbl)>0) begin set @[email protected]+'<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 @[email protected]+'<TD>'; set @[email protected]+cast(@ID as nvarchar(max)); set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+cast(@Driver_GUID as nvarchar(255)); set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+coalesce(@Server,''); set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+coalesce(@Name,''); set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+cast(@TotalSpaceGb as nvarchar(255)); set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+cast(@FreeSpaceGb as nvarchar(255)); set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+cast(@DiffFreeSpaceMb as nvarchar(255)); set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+cast(@FreeSpacePercent as nvarchar(255)); set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+cast(@DiffFreeSpacePercent as nvarchar(255)); set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+rep.GetDateFormat(@UpdateUTCDate, default)+' '+rep.GetTimeFormat(@UpdateUTCDate, default); set @[email protected]+'</TD>'; delete from @tbl where [email protected]; set @[email protected]+'</TR>'; end set @[email protected]+'</TABLE>'; set @[email protected]+'<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
Diese gespeicherte Prozedur generiert einen HTML-Bericht über logische Laufwerke, die entweder über weniger als 15 % freien Speicherplatz verfügen oder deren freier Speicherplatz täglich um mehr als 5 % abnimmt. Letzteres zeigt eine seltsame Aktivität von Datensätzen, was bedeutet, dass jemand sehr oft zu viele Informationen auf dieser Festplatte speichert. Dies kann aus folgenden Gründen geschehen:
- Es ist an der Zeit, eine Festplatte zu erweitern;
- Es ist notwendig, unbenutzte Dateien auf einem logischen Laufwerk zu löschen;
- Löschen und reduzieren Sie Protokolldateien sowie Informationsdateien und andere Tabellen.
Lösung
In diesem Artikel habe ich ein Beispiel für die Implementierung eines Systems zur täglichen automatischen Datenerfassung über lokale Laufwerke und Datenbankdateien analysiert. Mit diesen Informationen können Sie im Voraus herausfinden, auf welcher Festplatte weniger freier Speicherplatz vorhanden ist und welche Datenbankdateien stark anwachsen. Es ermöglicht, einen Fall zu vermeiden, in dem auf einer Festplatte kein Speicherplatz vorhanden ist, und einen Grund herauszufinden, warum ein Prozess viel Speicherplatz auf einer Festplatte benötigt.
Lesen Sie auch:
Automatische Datenerfassung von Datenbankschemaänderungen in MS SQL Server
Automatische Datenerfassung über erledigte Aufgaben im MS SQL Server