Einführung
Sie können viele Anleitungen zum Sichern und Wiederherstellen von Datenbanken finden. In diesem zeigen wir, wie dies mit den Standardmitteln von MS SQL Server geschehen kann.
Dieses Beispiel behandelt eine Reihe von Ansätzen – von der Überprüfung der Datenbankintegrität vor der Sicherung bis hin zur Wiederherstellung der Datenbank aus einer zuvor erstellten Sicherungskopie.
Die Lösung
Schauen wir uns zunächst den Gesamtalgorithmus an, den wir zum Sichern einer Datenbank verwenden:
1) Definieren, welche Datenbanken gesichert werden müssen
2) Prüfen der Integrität der ausgewählten Datenbanken
3) Erstellen einer Sicherung (vollständige, differenzielle oder Transaktionsprotokollkopie) für jede der ausgewählten Datenbanken
4) Überprüfen der erstellten Sicherungskopien
5) Komprimieren der Transaktionsprotokolle (falls erforderlich)
Nachfolgend finden Sie ein Implementierungsbeispiel dieses Algorithmus.
Um zu definieren, welche Datenbanken gesichert werden müssen, erstellen wir die folgende Tabelle:
USE [DB_NAME] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [srv].[BackupSettings]( [DBID] [int] NOT NULL, [FullPathBackup] [nvarchar](255) NOT NULL, [DiffPathBackup] [nvarchar](255) NULL, [LogPathBackup] [nvarchar](255) NULL, [InsertUTCDate] [datetime] NOT NULL, CONSTRAINT [PK_BackupSettings] PRIMARY KEY CLUSTERED ( [DBID] 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].[BackupSettings] ADD CONSTRAINT [DF_BackupSettings_InsertUTCDate] DEFAULT (getutcdate()) FOR [InsertUTCDate]; GO
Die Datenbankkennung befindet sich in der ersten Spalte, „FullPathBackup“ enthält den Pfad für die vollständige Sicherungskopieerstellung (z. B. „disk:\…\“) und DiffPathBackup und LogPathBackup enthalten vollständige Pfade für die Erstellung von Differenz- und Transaktionsprotokollkopien bzw. Wenn die Spalten DiffPathBackup oder LogPathBackup leer sind, wird die differenzielle und/oder Transaktionsprotokollkopie für diese Datenbank nicht erstellt.
Wir können auch eine Darstellung basierend auf dieser Tabelle erstellen:
USE [DB_NAME]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE view [srv].[vBackupSettings]
as
SELECT [DBID]
,DB_Name([DBID]) as [DBName]
,[FullPathBackup]
,[DiffPathBackup]
,[LogPathBackup]
,[InsertUTCDate]
FROM [srv].[BackupSettings];
GO Mit dieser Darstellung können Sie effektiv überprüfen, welche Datenbanken am Sicherungsvorgang teilnehmen.
Lassen Sie uns nun eine Darstellung erstellen, die Datenbankdateiinformationen aus der Systemdarstellung sys.master_files anzeigt:
USE [DB_NAME]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE view [inf].[ServerDBFileInfo] as
SELECT @@Servername AS Server ,
File_id ,--DB file identifier. The base value for file_id is 1
Type_desc ,--Type file description
Name as [FileName] ,--DB logical file name
LEFT(Physical_Name, 1) AS Drive ,--Drive flag of the DB file location
Physical_Name ,--Full file name in the OS
RIGHT(physical_name, 3) AS Ext ,--File extension
Size as CountPage, --Current file size in 8Kb pages
round((cast(Size*8 as float))/1024,3) as SizeMb, --File size in Mb
round((cast(Size*8 as float))/1024/1024,3) as SizeGb, --File size in Gb
case when is_percent_growth=0 then Growth*8 else 0 end as Growth, --File growth in 8Kb pages
case when is_percent_growth=0 then round((cast(Growth*8 as float))/1024,3) end as GrowthMb, --File growth in Mb
case when is_percent_growth=0 then round((cast(Growth*8 as float))/1024/1024,3) end as GrowthGb, --File growth in Gb
case when is_percent_growth=1 then Growth else 0 end as GrowthPercent, --File growth in percent
is_percent_growth, --Percent growth attribute
database_id,
DB_Name(database_id) as [DB_Name],
State,--File state
state_desc as StateDesc,--File state description
is_media_read_only as IsMediaReadOnly,--File is located on the drive as read-only (0 - and for writing)
is_read_only as IsReadOnly,--file is flagged as read-only (0 - and for writing)
is_sparse as IsSpace,--Sparse file
is_name_reserved as IsNameReserved,--1 - Remote file name, accessible for use.
--It is necessary to get a log backup before using the same name (name or physical_name arguments) again for a new file
--0 - Filename, inaccessible for use
create_lsn as CreateLsn,--Transaction registration number in the log (LSN) which was used to create the file
drop_lsn as DropLsn,--LSN which was used to delete the file
read_only_lsn as ReadOnlyLsn,--LSN which was used by the file group containing the file to change the "read and write" type to "read-only" (the latest change)
read_write_lsn as ReadWriteLsn,--LSN which was used by the file group containing the file to change the "read-only" type to "read and write" (the latest change)
differential_base_lsn as DifferentialBaseLsn,--A base for differential backup copies. Data extents which were changed after the LSN is included into the differential backup.
differential_base_guid as DifferentialBaseGuid,--Unique identifier of the base backup copy which will be used to create a differential copy.
differential_base_time as DifferentialBaseTime,--The time corresponding to differential_base_lsn
redo_start_lsn as RedoStartLsn,--LSN used to determine the start of the next redo
--Is NULL, except for the cases in which state = RESTORING or state = RECOVERY_PENDING
redo_start_fork_guid as RedoStartForkGuid,--Unique identifier for the restoration fork point
--first_fork_guid argument value of the next restored backup copy should be equal to this value
redo_target_lsn as RedoTargetLsn,--LSN which serves as a stop point for an "online" mode redo in this file
--Is NULL, except for the cases in which state = RESTORING or state = RECOVERY_PENDING
redo_target_fork_guid as RedoTargetForkGuid,--Restoration fork on which the container can be restored. Used along with redo_target_lsn
backup_lsn as BackupLsn--LSN of the most recent data or the file's differential backup copy
FROM sys.master_files--database_files;
GO Um vollständige Sicherungskopien zu erstellen, implementieren wir die folgende gespeicherte Prozedur:
[expand title =”Code „]
USE [DB_NAME]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [srv].[RunFullBackupDB]
@ClearLog bit=1 --specifies whether the transaction log size should be reduced
AS
BEGIN
/*
Creating a full DB backup copy and checking the DB for integrity beforehand
*/
SET NOCOUNT ON;
declare @dt datetime=getdate();
declare @year int=YEAR(@dt);
declare @month int=MONTH(@dt);
declare @day int=DAY(@dt);
declare @hour int=DatePart(hour, @dt);
declare @minute int=DatePart(minute, @dt);
declare @second int=DatePart(second, @dt);
declare @pathBackup nvarchar(255);
declare @pathstr nvarchar(255);
declare @DBName nvarchar(255);
declare @backupName nvarchar(255);
declare @sql nvarchar(max);
declare @backupSetId as int;
declare @FileNameLog nvarchar(255);
declare @tbllog table(
[DBName] [nvarchar](255) NOT NULL,
[FileNameLog] [nvarchar](255) NOT NULL
);
declare @tbl table (
[DBName] [nvarchar](255) NOT NULL,
[FullPathBackup] [nvarchar](255) NOT NULL
);
--Retrieving DB name and full paths for full backup copy creation
insert into @tbl (
[DBName]
,[FullPathBackup]
)
select DB_NAME([DBID])
,[FullPathBackup]
from [srv].[BackupSettings];
--Retrieving the DB name and names of the according transaction logs (as one DB can have multiple logs)
insert into @tbllog([DBName], [FileNameLog])
select t.[DBName], tt.[FileName] as [FileNameLog]
from @tbl as t
inner join [inf].[ServerDBFileInfo] as tt on t.[DBName]=DB_NAME(tt.[database_id])
where tt.[Type_desc]='LOG';
--sequentially processing each of the DBs we got earlier
while(exists(select top(1) 1 from @tbl))
begin
set @backupSetId=NULL;
select top(1)
@DBName=[DBName],
@pathBackup=[FullPathBackup]
from @tbl;
set @example@sqldat.com+N'_Full_backup_'+cast(@year as nvarchar(255))+N'_'+cast(@month as nvarchar(255))+N'_'+cast(@day as nvarchar(255))--+N'_'
--+cast(@hour as nvarchar(255))+N'_'+cast(@minute as nvarchar(255))+N'_'+cast(@second as nvarchar(255));
set @example@sqldat.comexample@sqldat.com+N'.bak';
--checking the DB for integrity
set @sql=N'DBCC CHECKDB(N'+N''''example@sqldat.com+N''''+N') WITH NO_INFOMSGS';
exec(@sql);
--executing the backup copy creation procedure
set @sql=N'BACKUP DATABASE ['example@sqldat.com+N'] TO DISK = N'+N''''example@sqldat.com+N''''+
N' WITH NOFORMAT, NOINIT, NAME = N'+N''''example@sqldat.com+N''''+
N', CHECKSUM, STOP_ON_ERROR, SKIP, REWIND, COMPRESSION, STATS = 10;';
exec(@sql);
--checking the backup copy we created
select @backupSetId = position
from msdb..backupset where example@sqldat.com
and backup_set_id=(select max(backup_set_id) from msdb..backupset where example@sqldat.com);
set @sql=N'Verification error. Backup copy information for "'example@sqldat.com+'" database not found.';
if @backupSetId is null begin raiserror(@sql, 16, 1) end
else
begin
set @sql=N'RESTORE VERIFYONLY FROM DISK = N'+''''example@sqldat.com+N''''+N' WITH FILE = '+cast(@backupSetId as nvarchar(255));
exec(@sql);
end
--compressing the DB transaction logs
if(@ClearLog=1)
begin
while(exists(select top(1) 1 from @tbllog where [DBName]example@sqldat.com))
begin
select top(1)
@FileNameLog=FileNameLog
from @tbllog
where example@sqldat.com;
set @sql=N'USE ['example@sqldat.com+N'];'+N' DBCC SHRINKFILE (N'+N''''example@sqldat.com+N''''+N' , 0, TRUNCATEONLY)';
exec(@sql);
delete from @tbllog
where example@sqldat.com
and example@sqldat.com;
end
end
delete from @tbl
where [DBName]example@sqldat.com;
end
END
GO [/expandieren]
Gemäß dem Code können wir sehen, dass dieses Verfahren eine Lösung für die verbleibenden Schritte des Algorithmus zum Erstellen einer Sicherungskopie bietet.
Prozeduren, die Differential- und Transaktionsprotokollkopien erstellen, werden auf ähnliche Weise implementiert:
[expand title =”Code „]
USE [DB_NAME]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [srv].[RunDiffBackupDB]
@ClearLog bit=1 --specifies if the transaction log size should be reduced
AS
BEGIN
/*
Creating a differential DB backup copy
*/
SET NOCOUNT ON;
declare @dt datetime=getdate();
declare @year int=YEAR(@dt);
declare @month int=MONTH(@dt);
declare @day int=DAY(@dt);
declare @hour int=DatePart(hour, @dt);
declare @minute int=DatePart(minute, @dt);
declare @second int=DatePart(second, @dt);
declare @pathBackup nvarchar(255);
declare @pathstr nvarchar(255);
declare @DBName nvarchar(255);
declare @backupName nvarchar(255);
declare @sql nvarchar(max);
declare @backupSetId as int;
declare @FileNameLog nvarchar(255);
declare @tbl table (
[DBName] [nvarchar](255) NOT NULL,
[DiffPathBackup] [nvarchar](255) NOT NULL
);
declare @tbllog table(
[DBName] [nvarchar](255) NOT NULL,
[FileNameLog] [nvarchar](255) NOT NULL
);
--Retrieving the DB name and full paths for creating differential backup copies
insert into @tbl (
[DBName]
,[DiffPathBackup]
)
select DB_NAME([DBID])
,[DiffPathBackup]
from [srv].[BackupSettings]
where [DiffPathBackup] is not null;
--Retrieving DB name and the full names of the according transaction log files (as one DB can have multiple logs)
insert into @tbllog([DBName], [FileNameLog])
select t.[DBName], tt.[FileName] as [FileNameLog]
from @tbl as t
inner join [inf].[ServerDBFileInfo] as tt on t.[DBName]=DB_NAME(tt.[database_id])
where tt.[Type_desc]='LOG';
--sequentially processing each of the DBs we got earlier
while(exists(select top(1) 1 from @tbl))
begin
set @backupSetId=NULL;
select top(1)
@DBName=[DBName],
@pathBackup=[DiffPathBackup]
from @tbl;
set @example@sqldat.com+N'_Diff_backup_'+cast(@year as nvarchar(255))+N'_'+cast(@month as nvarchar(255))+N'_'+cast(@day as nvarchar(255))+N'_'
+cast(@hour as nvarchar(255))+N'_'+cast(@minute as nvarchar(255))+N'_'+cast(@second as nvarchar(255));
set @example@sqldat.comexample@sqldat.com+N'.bak';
--checking the DB for integrity
set @sql=N'DBCC CHECKDB(N'+N''''example@sqldat.com+N''''+N') WITH NO_INFOMSGS';
exec(@sql);
--executing the backup procedure
set @sql=N'BACKUP DATABASE ['example@sqldat.com+N'] TO DISK = N'+N''''example@sqldat.com+N''''+
N' WITH DIFFERENTIAL, NOFORMAT, NOINIT, NAME = N'+N''''example@sqldat.com+N''''+
N', CHECKSUM, STOP_ON_ERROR, SKIP, REWIND, COMPRESSION, STATS = 10;';
exec(@sql);
--checking the backup copy we just created
select @backupSetId = position
from msdb..backupset where example@sqldat.com
and backup_set_id=(select max(backup_set_id) from msdb..backupset where example@sqldat.com);
set @sql=N'Verification error. Backup copy information for "'example@sqldat.com+'" database not found.';
if @backupSetId is null begin raiserror(@sql, 16, 1) end
else
begin
set @sql=N'RESTORE VERIFYONLY FROM DISK = N'+''''example@sqldat.com+N''''+N' WITH FILE = '+cast(@backupSetId as nvarchar(255));
exec(@sql);
end
--compressing the DB transaction logs
if(@ClearLog=1)
begin
while(exists(select top(1) 1 from @tbllog where [DBName]example@sqldat.com))
begin
select top(1)
@FileNameLog=FileNameLog
from @tbllog
where example@sqldat.com;
set @sql=N'USE ['example@sqldat.com+N'];'+N' DBCC SHRINKFILE (N'+N''''example@sqldat.com+N''''+N' , 0, TRUNCATEONLY)';
exec(@sql);
delete from @tbllog
where example@sqldat.com
and example@sqldat.com;
end
end
delete from @tbl
where [DBName]example@sqldat.com;
end
END
GO [/expandieren]
Da das Überprüfen von Datenbanken auf Integrität viele Ressourcen beansprucht, können wir es weglassen, während wir eine differenzielle Sicherungskopie erstellen.
[expand title =”Code „]
USE [DB_NAME]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [srv].[RunLogBackupDB]
@ClearLog bit=1 --specifies if the transaction log size should be reduced
AS
BEGIN
/*
Backing up the DB transaction log
*/
SET NOCOUNT ON;
declare @dt datetime=getdate();
declare @year int=YEAR(@dt);
declare @month int=MONTH(@dt);
declare @day int=DAY(@dt);
declare @hour int=DatePart(hour, @dt);
declare @minute int=DatePart(minute, @dt);
declare @second int=DatePart(second, @dt);
declare @pathBackup nvarchar(255);
declare @pathstr nvarchar(255);
declare @DBName nvarchar(255);
declare @backupName nvarchar(255);
declare @sql nvarchar(max);
declare @backupSetId as int;
declare @FileNameLog nvarchar(255);
declare @tbl table (
[DBName] [nvarchar](255) NOT NULL,
[LogPathBackup] [nvarchar](255) NOT NULL
);
declare @tbllog table(
[DBName] [nvarchar](255) NOT NULL,
[FileNameLog] [nvarchar](255) NOT NULL
);
--Retrieving DB names and full paths for creating backup copies of transaction logs with a non-simple recovery model (full or bulk-logged). System DBs are also excluded
insert into @tbl (
[DBName]
,[LogPathBackup]
)
select DB_NAME(b.[DBID])
,b.[LogPathBackup]
from [srv].[BackupSettings] as b
inner join sys.databases as d on b.[DBID]=d.[database_id]
where d.recovery_model<3
and DB_NAME([DBID]) not in (
N'master',
N'tempdb',
N'model',
N'msdb',
N'ReportServer',
N'ReportServerTempDB'
)
and [LogPathBackup] is not null;
--Retrieving DB name and the full names of the according transaction log files (as one DB can have multiple logs)
insert into @tbllog([DBName], [FileNameLog])
select t.[DBName], tt.[FileName] as [FileNameLog]
from @tbl as t
inner join [inf].[ServerDBFileInfo] as tt on t.[DBName]=DB_NAME(tt.[database_id])
where tt.[Type_desc]='LOG';
--sequentially processing each of the DBs we got earlier
while(exists(select top(1) 1 from @tbl))
begin
set @backupSetId=NULL;
select top(1)
@DBName=[DBName],
@pathBackup=[LogPathBackup]
from @tbl;
set @example@sqldat.com+N'_Log_backup_'+cast(@year as nvarchar(255))+N'_'+cast(@month as nvarchar(255))+N'_'+cast(@day as nvarchar(255))+N'_'
+cast(@hour as nvarchar(255))+N'_'+cast(@minute as nvarchar(255))+N'_'+cast(@second as nvarchar(255));
set @example@sqldat.comexample@sqldat.com+N'.trn';
--executing the backup procedure
set @sql=N'BACKUP LOG ['example@sqldat.com+N'] TO DISK = N'+N''''example@sqldat.com+N''''+
N' WITH NOFORMAT, NOINIT, NAME = N'+N''''example@sqldat.com+N''''+
N', CHECKSUM, STOP_ON_ERROR, SKIP, REWIND, COMPRESSION, STATS = 10;';
exec(@sql);
--Checking the transaction log backup copy we just created
select @backupSetId = position
from msdb..backupset where example@sqldat.com
and backup_set_id=(select max(backup_set_id) from msdb..backupset where example@sqldat.com);
set @sql=N'Verification error. Backup copy information for "'example@sqldat.com+'" database not found.';
if @backupSetId is null begin raiserror(@sql, 16, 1) end
else
begin
set @sql=N'RESTORE VERIFYONLY FROM DISK = N'+''''example@sqldat.com+N''''+N' WITH FILE = '+cast(@backupSetId as nvarchar(255));
exec(@sql);
end
--compressing the DB transaction logs
if(@ClearLog=1)
begin
while(exists(select top(1) 1 from @tbllog where [DBName]example@sqldat.com))
begin
select top(1)
@FileNameLog=FileNameLog
from @tbllog
where example@sqldat.com;
set @sql=N'USE ['example@sqldat.com+N'];'+N' DBCC SHRINKFILE (N'+N''''example@sqldat.com+N''''+N' , 0, TRUNCATEONLY)';
exec(@sql);
delete from @tbllog
where example@sqldat.com
and example@sqldat.com;
end
end
delete from @tbl
where [DBName]example@sqldat.com;
end
END
GO [/expandieren]
Wie oben erwähnt, ist das Überprüfen von Datenbanken auf Integrität eine ressourcenintensive Aufgabe. Zusammen mit der Tatsache, dass Transaktionslog-Sicherungskopien normalerweise ziemlich oft erstellt werden müssen, gibt uns dies einen Grund, die Integritätsprüfung beim Erstellen einer Transaktionslog-Kopie wegzulassen.
Bitte denken Sie auch daran, dass in regelmäßigen Abständen vollständige Sicherungskopien der Datenbanken „master“, „msdb“ und „model“ erstellt werden müssen.
Um den Erstellungsprozess der Sicherungskopie zu automatisieren, müssen Sie nur einen Aufruf der zuvor implementierten Prozeduren in den Windows-Taskplaner, Agent-Jobs oder einen ähnlichen verfügbaren Dienst platzieren.
Sie müssen die Anrufhäufigkeit für jedes dieser Verfahren individuell basierend auf Lastspitzen, Aktivitätsplateaus usw. einstellen.
Der grundlegende Ansatz ist wie folgt:
1) Einmal täglich eine vollständige Sicherungskopie erstellen
2) Alle 2–4 Stunden differenzielle Sicherungskopien erstellen
3) Alle 5–60 Minuten Sicherungskopien des Transaktionsprotokolls erstellen
Bitte beachten Sie, dass in der Regel Datenbanken am Ausfallsicherheits- und Schnellzugriffssystem teilnehmen. Und wenn der spätere Sicherungskopien von Transaktionsprotokollen verwendet, ist es äußerst wichtig, den Vorgang nicht zu stören. Genauer gesagt bedeutet dies, dass Transaktionsprotokollkopien nicht von mehreren verschiedenen Prozessen erstellt werden sollten – wenn dies geschieht, geht die Sicherungssequenz dieser Kopien verloren.
Hier haben wir Beispiele dafür gesehen, wie jede Datenbank nacheinander verarbeitet wird, eine nach der anderen. Wir können jedoch eine parallele Verarbeitung in der Produktionsumgebung erreichen – so dass mehrere Sicherungskopien gleichzeitig erstellt werden können. Dies kann auf verschiedene Arten angegangen werden. Beispielsweise durch Aufrufen der folgenden gespeicherten Prozedur:
USE [DB_NAME]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [inf].[RunAsyncExecute]
(
@sql nvarchar(max),
@jobname nvarchar(57) = null,
@database nvarchar(128)= null,
@owner nvarchar(128) = null
)
AS BEGIN
/*
Asynchronous package execution via the Agent's jobs
RunAsyncExecute - asynchronous execution of T-SQL command or stored prodecure
2012 Antonin Foller, Motobit Software, www.motobit.com
https://www.motobit.com/tips/detpg_async-execute-sql/
*/
SET NOCOUNT ON;
declare @id uniqueidentifier;
--Create unique job name if the name is not specified
if (@jobname is null) set @jobname= '';
set @jobname = @jobname + '_async_' + convert(varchar(64),NEWID());
if (@owner is null) set @owner = 'sa';
--Create a new job, get job ID
execute msdb..sp_add_job @jobname, @example@sqldat.com, @example@sqldat.com OUTPUT;
--Specify a job server for the job
execute msdb..sp_add_jobserver @example@sqldat.com;
--Specify a first step of the job - the SQL command
--(@on_success_action = 3 ... Go to next step)
execute msdb..sp_add_jobstep @example@sqldat.com, @step_name='Step1', @command = @sql,
@database_name = @database, @on_success_action = 3;
--Specify next step of the job - delete the job
declare @deletecommand varchar(200);
set @deletecommand = 'execute msdb..sp_delete_job @job_name='''example@sqldat.com+'''';
execute msdb..sp_add_jobstep @example@sqldat.com, @step_name='Step2', @command = @deletecommand;
--Start the job
execute msdb..sp_start_job @example@sqldat.com;
END
GO Hier wird Asynchronität erreicht, indem die Agent-Jobs dynamisch erstellt, ausgeführt und anschließend gelöscht werden.
Sehen wir uns nun den allgemeinen Algorithmus zum Wiederherstellen von Datenbanken aus Sicherungskopien an, die zuvor in einer anderen/Testumgebung erstellt wurden:
1) Festlegen, welche Datenbanken wiederhergestellt werden sollen und wo sich ihre Sicherungskopien befinden
2) Wiederherstellen der Datenbanken
3) Prüfen der wiederhergestellten Datenbanken auf Integrität
Nun sehen wir uns eine Implementierung eines Algorithmus an, der eine Datenbank aus einer vollständigen Sicherungskopie wiederherstellt. Für eine differenzielle Kopie ist das Verfahren ähnlich – der einzige Unterschied besteht darin, dass zuerst eine vollständige Sicherungskopie wiederhergestellt werden muss, gefolgt von der differenziellen Kopie.
Um zu definieren, welche Datenbanken wiederhergestellt werden sollen, sowie den Speicherort ihrer Sicherungskopien, erstellen wir zwei Tabellen wie unten gezeigt:
USE [DB_NAME] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [srv].[RestoreSettings]( [DBName] [nvarchar](255) NOT NULL, [FullPathRestore] [nvarchar](255) NOT NULL, [DiffPathRestore] [nvarchar](255) NOT NULL, [LogPathRestore] [nvarchar](255) NOT NULL, [InsertUTCDate] [datetime] NOT NULL, CONSTRAINT [PK_RestoreSettings] PRIMARY KEY CLUSTERED ( [DBName] 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].[RestoreSettings] ADD CONSTRAINT [DF_RestoreSettings_InsertUTCDate] DEFAULT (getutcdate()) FOR [InsertUTCDate]; GO
Der Zweck der Spalten ist hier analog zu denen aus der Tabelle [srv].[BackupSettings]. Der einzige Unterschied besteht darin, dass der vollständige Pfad verwendet wird, um die Sicherungskopien für die Wiederherstellung zu finden, und nicht, um neue zu erstellen.
USE [DB_NAME] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [srv].[RestoreSettingsDetail]( [Row_GUID] [uniqueidentifier] NOT NULL, [DBName] [nvarchar](255) NOT NULL, [SourcePathRestore] [nvarchar](255) NOT NULL, TargetPathRestore [nvarchar](255) NOT NULL, [Ext] [nvarchar](255) NOT NULL, [InsertUTCDate] [datetime] NOT NULL, CONSTRAINT [PK_RestoreSettingsDetail] PRIMARY KEY CLUSTERED ( [Row_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].[RestoreSettingsDetail] ADD CONSTRAINT [DF_RestoreSettingsDetail_Row_GUID] DEFAULT (newid()) FOR [Row_GUID]; GO ALTER TABLE [srv].[RestoreSettingsDetail] ADD CONSTRAINT [DF_RestoreSettingsDetail_InsertUTCDate] DEFAULT (getutcdate()) FOR [InsertUTCDate]; GO
Diese Tabelle wird benötigt, um die vollständigen Dateinamen der wiederherzustellenden Datenbank zu definieren, die dann für die weitere Übertragung verwendet werden (z. B. [SourcePathRestore]='Logical file name' und [TargetPathRestore]='disk:\…\Physical file name ', während [Ext]='Dateierweiterung')
Tatsächlich können wir logische Namen der Datenbankdateien mit der folgenden Abfrage definieren:
RESTORE FILELISTONLY FROM DISK ='disk:\...\backup copy.BAK';
Informationen über Sicherungskopien, die sich in einer Datei befinden, können auf folgende Weise abgerufen werden:
RESTORE HEADERONLY FROM DISK='disk:\...\backup copy.BAK';
Als nächstes haben wir eine Implementierung einer gespeicherten Prozedur, die zum Wiederherstellen einer Datenbank aus einer vollständigen Sicherungskopie und zum Prüfen auf Datenintegrität verwendet wird:
[expand title =”Code „]
USE [DB_NAME]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [srv].[RunFullRestoreDB]
AS
BEGIN
/*
Recovering a DB from a full backup copy and checking the DB for integrity
*/
SET NOCOUNT ON;
declare @dt datetime=DateAdd(day,-2,getdate());
declare @year int=YEAR(@dt);
declare @month int=MONTH(@dt);
declare @day int=DAY(@dt);
declare @hour int=DatePart(hour, @dt);
declare @minute int=DatePart(minute, @dt);
declare @second int=DatePart(second, @dt);
declare @pathBackup nvarchar(255);
declare @pathstr nvarchar(255);
declare @DBName nvarchar(255);
declare @backupName nvarchar(255);
declare @sql nvarchar(max);
declare @backupSetId as int;
declare @FileNameLog nvarchar(255);
declare @SourcePathRestore nvarchar(255);
declare @TargetPathRestore nvarchar(255);
declare @Ext nvarchar(255);
declare @tbl table (
[DBName] [nvarchar](255) NOT NULL,
[FullPathRestore] [nvarchar](255) NOT NULL
);
declare @tbl_files table (
[DBName] [nvarchar](255) NOT NULL,
[SourcePathRestore] [nvarchar](255) NOT NULL,
[TargetPathRestore] [nvarchar](255) NOT NULL,
[Ext] [nvarchar](255) NOT NULL
);
--retrieving a list of DB names and the paths to full backup copies
insert into @tbl (
[DBName]
,[FullPathRestore]
)
select [DBName]
,[FullPathRestore]
from [srv].[RestoreSettings];
--retrieving detailed info about the new DB files location
insert into @tbl_files (
[DBName]
,[SourcePathRestore]
,[TargetPathRestore]
,[Ext]
)
select [DBName]
,[SourcePathRestore]
,[TargetPathRestore]
,[Ext]
from [srv].[RestoreSettingsDetail];
--processing each of the DBs we got earlier
while(exists(select top(1) 1 from @tbl))
begin
set @backupSetId=NULL;
select top(1)
@DBName=[DBName],
@pathBackup=[FullPathRestore]
from @tbl;
set @example@sqldat.com+N'_Full_backup_'+cast(@year as nvarchar(255))+N'_'+cast(@month as nvarchar(255))+N'_'+cast(@day as nvarchar(255))--+N'_'
--+cast(@hour as nvarchar(255))+N'_'+cast(@minute as nvarchar(255))+N'_'+cast(@second as nvarchar(255));
set @example@sqldat.comexample@sqldat.com+N'.bak';
--creating a backup query and executing it
set @sql=N'RESTORE DATABASE ['example@sqldat.com+N'_Restore] FROM DISK = N'+N''''example@sqldat.com+N''''+
N' WITH FILE = 1,';
while(exists(select top(1) 1 from @tbl_files where [DBName]example@sqldat.com))
begin
select top(1)
@SourcePathRestore=[SourcePathRestore],
@TargetPathRestore=[TargetPathRestore],
@Ext=[Ext]
from @tbl_files
where [DBName]example@sqldat.com;
set @example@sqldat.com+N' MOVE N'+N''''example@sqldat.com+N''''+N' TO N'+N''''example@sqldat.com+N'_Restore.'example@sqldat.com+N''''+N',';
delete from @tbl_files
where [DBName]example@sqldat.com
and [SourcePathRestore]example@sqldat.com
and [Ext]example@sqldat.com;
end
set @example@sqldat.com+N' NOUNLOAD, REPLACE, STATS = 5';
exec(@sql);
--checking the DB for integrity
set @sql=N'DBCC CHECKDB(N'+N''''example@sqldat.comName+'_Restore'+N''''+N') WITH NO_INFOMSGS';
exec(@sql);
delete from @tbl
where [DBName]example@sqldat.com;
end
END [/expandieren]
Um festzulegen, welche vollständige Sicherungskopie für die Wiederherstellung verwendet werden soll, wird ein speziell strukturierter Dateiname verwendet:
Um diesen Datenbankwiederherstellungsprozess zu automatisieren, sollte der Aufruf der von uns implementierten gespeicherten Prozedur in den Windows-Taskplaner, Agent-Jobs oder einen ähnlichen verfügbaren Dienst platziert werden.
Sie können die neuesten Datenbank-Sicherungskopien anhand der folgenden Darstellung anzeigen:
USE [DB_NAME]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE VIEW [inf].[vServerLastBackupDB] as
with backup_cte as
(
select
bs.[database_name],
backup_type =
case bs.[type]
when 'D' then 'database'
when 'L' then 'log'
when 'I' then 'differential'
else 'other'
end,
bs.[first_lsn],
bs.[last_lsn],
bs.[backup_start_date],
bs.[backup_finish_date],
cast(bs.[backup_size] as decimal(18,3))/1024/1024 as BackupSizeMb,
rownum =
row_number() over
(
partition by bs.[database_name], type
order by bs.[backup_finish_date] desc
),
LogicalDeviceName = bmf.[logical_device_name],
PhysicalDeviceName = bmf.[physical_device_name],
bs.[server_name],
bs.[user_name]
FROM msdb.dbo.backupset bs
INNER JOIN msdb.dbo.backupmediafamily bmf
ON [bs].[media_set_id] = [bmf].[media_set_id]
)
select
[server_name] as [ServerName],
[database_name] as [DBName],
[user_name] as [USerName],
[backup_type] as [BackupType],
[backup_start_date] as [BackupStartDate],
[backup_finish_date] as [BackupFinishDate],
[BackupSizeMb], --uncompressed size
[LogicalDeviceName],
[PhysicalDeviceName],
[first_lsn] as [FirstLSN],
[last_lsn] as [LastLSN]
from backup_cte
where rownum = 1; Das Ergebnis
In diesem Leitfaden haben wir uns die Implementierung eines automatisierten Backup-Prozesses auf einem Server und die anschließende Wiederherstellung auf einem anderen (z. B. einem Testserver) angesehen.
Diese Methode ermöglicht es uns, den Erstellungsprozess der Sicherungskopien zu automatisieren, die Sicherungskopien durch Wiederherstellung zu überprüfen und die oben gezeigten Prozesse zu optimieren.
Quellen:
Backup
Restore
Backupset
CHECKDB
SHRINKFILE
sys.master_files