In der sehr fehleranfälligen Welt der IT sind Vorbereitungen auf gewisse Katastrophenszenarios durch Sicherungen und Backups unerlässlich und gehören, gerade im Bereich der Datanbankadministration, zu einer der wichtigsten Vorkehrungen.
Es gibt jedoch Fälle, in denen eine einfache Sicherungsdatei nicht ausreicht, da möglicherweise im Vorhinein gar nicht bekannt ist, wie viel Speicherplatz tatsächlich für die Dateien der Datenbank (also .mfd- und .idf-Dateien) benötigt wird, bis diese erfolgreich wiederhergestellt wurden.
In einem anderen durchaus denkbaren Szenario könnte sich die Sicherungsdatei an einem Remotestandort befinden. Besteht nun kein Zugriff mehr auf den Datenbankserver, auf dem sich diese befindet, ist die aktuelle Größe der Dateien nicht länger einsehbar.
In den folgenden Abschnitten werden wir nun eine Reihe von T-SQL-Skripten vorstellen, mit denen nützliche Informationen für vollständige Sicherungen sowie die tatsächliche Dateigröße nachverfolgt werden können.
Ebenfalls wird das Szenario behandelt, in dem zwar eine Sicherungsdatei zur Verfügung steht, jedoch der Zugriff auf die Quellinstanz nicht möglich ist.
Zur Veranschaulichung der vorgestellten Skripte werden wir gemeinsam mit Ihnen eine leere Datenbank mit dem Namen “LargeDB“ erstellen, welche eine primäre .mfd-Datei (80 GB) und eine .Idf-Datei (9 GB) enthält.
Es folgen nun 3 verschiedene Ansätze, die beim Einsehen dieser Informationen helfen können, und das Problem aus verschiedenen Blickwinkeln betrachten.
Hierfür gehen Sie wie folgt vor:
Die Ausgabe der Abfrage sollte so aussehen:
Es ist sofort ersichtlich, dass die Sicherungsdatei der “LargeDB“-Datenbank 84 MB groß ist, die Größe der enthaltenen Daten jedoch 80 GB und die Protokolldatei 9 GB betragen. Die Sicherung einer 84 MB-großen Datenbank kann also schnell 90 GB Speicherplatz belegen, ohne dass dies direkt ersichtlich ist.
T-SQL Skript zum ersten Ansatz:
IF OBJECT_ID('tempdb..#FreeSpace') IS NOT NULL DROP TABLE #FreeSpace
CREATE TABLE #FreeSpace([database] VARCHAR(64) NOT NULL,amount INT NOT NULL)
DECLARE @sqlCommand varchar(2048)
SELECT @sqlCommand = 'USE [?]
DECLARE @freeSpace INT
SELECT @freeSpace = SUM(size/128 -(FILEPROPERTY(name, ''SpaceUsed'')/128)) FROM sys.master_files
INSERT INTO #FreeSpace VALUES(''?'', @freeSpace)
'
EXEC sp_MSforeachdb @sqlCommand
SELECT DISTINCT
d.name AS 'DatabaseName',
(SELECT CONVERT( DECIMAL(10,2),SUM(size)*8.0/1024)
FROM sys.master_files
WHERE type_desc = 'ROWS'
AND database_id = mf.database_id
GROUP BY database_id) AS 'DataSizeInMB',
(SELECT CONVERT(DECIMAL(10,2),SUM(size)*8.0/1024)
FROM sys.master_files
WHERE type_desc = 'LOG'
AND database_id = mf.database_id
GROUP BY database_id) AS 'LogSizeInMB',
(SELECT amount
FROM #FreeSpace
WHERE [database] = d.name) AS 'FreeSpaceInMB',
CONVERT(DECIMAL(10,2),b.compressed_backup_size/1024.0/1024.0) AS CompressedBackupSizeInMB,
d.state_desc AS 'State',
suser_sname(d.owner_sid) AS 'Owner',
d.compatibility_level AS 'CompatibilityLevel',
d.create_date AS 'DBCreatedDate'
FROM sys.databases d
JOIN sys.master_files mf ON d.database_id = mf.database_id
LEFT JOIN (
SELECT bs.compressed_backup_size,bs.database_name
FROM msdb.dbo.backupset bs
WHERE bs.backup_set_id IN (SELECT backup_set_id FROM msdb.dbo.backupset WHERE backup_start_date = (SELECT MAX(backup_start_date) FROM msdb.dbo.backupset WHERE database_name = bs.database_name))
) AS b ON b.database_name = d.name
WHERE d.name NOT IN ('tempdb')
ORDER BY d.name
DROP TABLE #FreeSpace/
Der zweite Ansatz verfolgt das selbe Ziel des Ersten, also das Auflisten der Größe der SQL-Server Datenbankdateien und des Backups, jedoch aus der “Perspektive“ des Backups.
Der Vorgang:
Die Ausgabe der Abfrage sollte so aussehen:
(Aufgrund der Breite der Ausgabe, ist diese hier auf zwei Screenshots aufgeteilt)
Die Ergebnismenge konzentriert sich auf relevante Informationen für die vollständigen Sicherungen, wird jedoch durch die tatsächliche Datengröße, welche die Sicherungsdatei tatsächlich darstellt, ergänzt. Für die “LargeDB“ – Datenbank werden also genau die gleichen 80 GB für die Quelldatei und 9 GB für die Protokolldatei angezeigt (die gleichen wie bei Ansatz 1).
T-SQL Skript zum 2. Ansatz:
WITH
MostRecentBackups
AS(
SELECT
database_name AS [Database],
MAX(bus.backup_finish_date) AS LastBackupTime,
CASE bus.type
WHEN 'D' THEN 'Full'
END AS Type
FROM msdb.dbo.backupset bus
WHERE bus.type <> 'F'
GROUP BY bus.database_name,bus.type
),
BackupsWithSize
AS(
SELECT
mrb.*,
(SELECT TOP 1 CONVERT(DECIMAL(10,2), b.compressed_backup_size/1024/1024) AS backup_size FROM msdb.dbo.backupset b WHERE [Database] = b.database_name AND LastBackupTime = b.backup_finish_date) AS [Backup Size],
(SELECT TOP 1 DATEDIFF(s, b.backup_start_date, b.backup_finish_date) FROM msdb.dbo.backupset b WHERE [Database] = b.database_name AND LastBackupTime = b.backup_finish_date) AS [Seconds],
(SELECT TOP 1 b.media_set_id FROM msdb.dbo.backupset b WHERE [Database] = b.database_name AND LastBackupTime = b.backup_finish_date) AS media_set_id
FROM MostRecentBackups mrb
)
SELECT
d.name AS [Database],
d.state_desc AS State,
bf.LastBackupTime AS [LastFull],
DATEDIFF(DAY,bf.LastBackupTime,GETDATE()) AS [TimeSinceLastFullInDays],
bf.[Backup Size] AS [FullBackupSizeInMB],
bf.Seconds AS [FullBackupSecondsToComplete],
CASE WHEN DATEDIFF(DAY,bf.LastBackupTime,GETDATE()) > 14 THEN NULL ELSE (SELECT TOP 1 bmf.physical_device_name FROM msdb.dbo.backupmediafamily bmf WHERE bmf.media_set_id = bf.media_set_id AND bmf.device_type = 2) END AS [FullBackupLocalPath],
(SELECT CONVERT(DECIMAL(10,2),SUM(size)*8.0/1024) AS size FROM sys.master_files WHERE type = 0 AND d.name = DB_NAME(database_id)) AS DataFileSize,
(SELECT CONVERT(DECIMAL(10,2),SUM(size)*8.0/1024) AS size FROM sys.master_files WHERE type = 1 AND d.name = DB_NAME(database_id)) AS LogFileSize
FROM sys.databases d
LEFT JOIN BackupsWithSize bf ON (d.name = bf.[Database] AND (bf.Type = 'Full' OR bf.Type IS NULL))
WHERE d.name <> 'tempdb' AND d.source_database_id IS NULL
ORDER BY d.name/
Der dritte und letzte Ansatz basiert ausschließlich auf der Sicherungsdatei:
T-SQL Skript zum 3. Ansatz:
-- enter the path and file name of the backup
DECLARE @filename nvarchar(500) = 'E:\Program Files\Microsoft SQL Server\MSSQL14.SQL_2017_1\MSSQL\Backup\test.bak'
IF NOT EXISTS (SELECT * FROM dbo.sysobjects where id = object_id(N'RestoreFilelistOnly') and OBJECTPROPERTY(id, N'IsTable') = 1)
BEGIN
SET NOCOUNT ON;
DECLARE @sqlCommand NVARCHAR(2048);
IF(
(SELECT
CASE
WHEN CONVERT(VARCHAR(128), SERVERPROPERTY ('PRODUCTVERSION')) LIKE '8%' THEN 0
WHEN CONVERT(VARCHAR(128), SERVERPROPERTY ('PRODUCTVERSION')) LIKE '9%' THEN 0
WHEN CONVERT(VARCHAR(128), SERVERPROPERTY ('PRODUCTVERSION')) LIKE '10.0%' THEN 0
WHEN CONVERT(VARCHAR(128), SERVERPROPERTY ('PRODUCTVERSION')) LIKE '10.5%' THEN 0
WHEN CONVERT(VARCHAR(128), SERVERPROPERTY ('PRODUCTVERSION')) LIKE '11%' THEN 0
WHEN CONVERT(VARCHAR(128), SERVERPROPERTY ('PRODUCTVERSION')) LIKE '12%' THEN 0
WHEN CONVERT(VARCHAR(128), SERVERPROPERTY ('PRODUCTVERSION')) LIKE '13%' THEN 1
WHEN CONVERT(VARCHAR(128), SERVERPROPERTY ('PRODUCTVERSION')) LIKE '14%' THEN 1
WHEN CONVERT(VARCHAR(128), SERVERPROPERTY ('PRODUCTVERSION')) LIKE '15%' THEN 1
ELSE 1
END
) = 0
)
SET @sqlCommand = '
CREATE TABLE ##RestoreFilelistOnly (
[LogicalName] NVARCHAR(128),
[PhysicalName] NVARCHAR(260),
[Type] CHAR(1),
[FileGroupName] NVARCHAR(128),
[Size] NUMERIC(20,0),
[MaxSize] NUMERIC(20,0),
[FileID] BIGINT,
[CreateLSN] NUMERIC(25,0),
[DropLSN] NUMERIC(25,0),
[UniqueID] UNIQUEIDENTIFIER,
[ReadOnlyLSN] NUMERIC(25,0),
[ReadWriteLSN] NUMERIC(25,0),
[BackupSizeInBytes] BIGINT,
[SourceBlockSize] INT,
[FileGroupID] INT,
[LogGroupGUID] UNIQUEIDENTIFIER,
[DifferentialBaseLSN] NUMERIC(25,0),
[DifferentialBaseGUID] UNIQUEIDENTIFIER,
[IsReadOnly] BIT,
[IsPresent] BIT,
[TDEThumbprint] VARBINARY(32)
)'
ELSE
SET @sqlCommand = '
CREATE TABLE ##RestoreFilelistOnly (
[LogicalName] NVARCHAR(128),
[PhysicalName] NVARCHAR(260),
[Type] CHAR(1),
[FileGroupName] NVARCHAR(128),
[Size] NUMERIC(20,0),
[MaxSize] NUMERIC(20,0),
[FileID] BIGINT,
[CreateLSN] NUMERIC(25,0),
[DropLSN] NUMERIC(25,0),
[UniqueID] UNIQUEIDENTIFIER,
[ReadOnlyLSN] NUMERIC(25,0),
[ReadWriteLSN] NUMERIC(25,0),
[BackupSizeInBytes] BIGINT,
[SourceBlockSize] INT,
[FileGroupID] INT,
[LogGroupGUID] UNIQUEIDENTIFIER,
[DifferentialBaseLSN] NUMERIC(25,0),
[DifferentialBaseGUID] UNIQUEIDENTIFIER,
[IsReadOnly] BIT,
[IsPresent] BIT,
[TDEThumbprint] VARBINARY(32),
[SnapshotUrl] NVARCHAR(360)
)'
EXEC sp_executesql @sqlCommand;
INSERT INTO ##RestoreFilelistOnly EXEC('RESTORE FILELISTONLY FROM DISK = ''' + @filename + '''')
SELECT PhysicalName, CONVERT(DECIMAL(10,3),(Size/1024/1024)) as FileSizeMB, CONVERT(DECIMAL(10,3),(BackupSizeInBytes/1024/1024)) as BackupSizeMB
FROM ##RestoreFilelistOnly
DROP TABLE ##RestoreFilelistOnly
END
Hier haben wir eine leere Datenbank namens “test” mit einer Quelldatei (.mdf-Datei) von 6 GB und einer Protokolldatei (.ldf-Datei) von 10 MB erstellt. Nach dem Ausführen einer vollständigen Sicherung zum Testen des obigen Skripts sehen Sie hier die Ausgabe:
Und tatsächlich stimmen die Informationen genau mit der Größe unserer “test”-Datenbank überein.
Kontaktieren Sie uns gerne über das
Kontaktformular und vereinbaren ein unverbindliches
Beratungsgespräch mit unseren Berater:innen zur
Bedarfsevaluierung. Gemeinsam optimieren wir Ihre
Umgebung und steigern Ihre Performance!
Wir freuen uns auf Ihre Kontaktaufnahme!