Introduktion
I de sidste to eller tre måneder er jeg blevet spurgt to gange om en løsning, der er native til SQL Server, der konsoliderer en backup-rapport for flere SQL Server-instanser på tværs af en virksomhed. Dette spørgsmål kom fra venner, der ikke nødvendigvis ønskede at bruge penge på at købe et værktøj, men som var mere tilbøjelige til at udnytte funktionerne i SQL Server. Jeg har tænkt på to mulige måder at opnå dette på:
- Brug af linkede servere, katalogvisninger, SQL Agent-job og databasemail
- Brug af Central Management Server
I denne artikel vil jeg demonstrere den første og håber, at vi får en anden del af artiklen engang senere.
Scenarie
Mit miljø består af et sæt af tre instanser, der sidder på separate servere på AWS. Disse "servere" er faktisk Amazon EC2'er, der kører SQL Server 2017 RTM CU5. Vi vil også drage fordel af Amazon Simple Email Service til at konfigurere Database Mail. I produktionen kan du helt sikkert bruge dine on-premise e-mail-servere og nå de samme mål. Du vil bemærke senere i denne artikel, at værtsnavnet (og dermed instansnavnene) er det samme. Dette skyldes, at serverne blev klonet fra det samme Amazon-maskinebillede (undskyld "dovenskaben"). Dette vil sandsynligvis ikke være tilfældet i produktionen.
Tag et par sikkerhedskopier
Lad os starte med at tage et par sikkerhedskopier af databaser, der sidder på disse tre forekomster. Dette vil generere de data, vi skal arbejde med. Vi skal derefter verificere, at sikkerhedskopierne er fanget i systemtabellerne msdb.dbo.backupset og msdb.dbo.backupmediafamily . De fulde beskrivelser af disse tabeller kan gennemgås i denne Microsoft-dokumentation eller blot bruge sp_columns .
-- Listing 1: Taking Backups on the Instances -- Backup a single DB with one stripe backup database newdb to disk='newdb.bak' -- Backup all DBs in the instance with timestamp in the backupset name exec sp_MSforeachdb @command1= 'declare @path varchar(300) set @path=''M:\MSSQL\BACKUP\?_Backup'' + convert(varchar(10),getdate(),110) + ''.bak'' print @path backup database [?] to [email protected]' -- Backup a single large DB with four stripes backup database [PieceMealDB] to disk='M:\MSSQL\BACKUP\PieceMealDB_01.bak', disk='M:\MSSQL\BACKUP\PieceMealDB_02.bak', disk='M:\MSSQL\BACKUP\PieceMealDB_03.bak', disk='M:\MSSQL\BACKUP\PieceMealDB_04.bak' with stats=10
Fig 3. Beskriver msdb.dbo.backupset
Tjekker sikkerhedskopier
Følgende script udnytter to katalogvisninger backupset og backupmediafamily at undersøge historikken for sikkerhedskopier oprettet på en forekomst af SQL Server. Sikkerhedskopieringskataloget indeholder en række for hvert sikkerhedskopisæt. Et sikkerhedskopieringssæt er defineret som indholdet af en sikkerhedskopieringsoperation, der føjes til et mediesæt. Et mediesæt er en ordresamling af medier, som en eller flere sikkerhedskopieringsoperationer har skrevet til.
-- Listing 2: Check Backups using msdb tables -- PRINT 'Checking Databases Successfully Backed Up' use msdb go select bus.database_name,bus.type, case bus.type when 'D' then 'Full' when 'I' then 'Differential' when 'L' then 'Log' end backup_type, bus.backup_start_date, bus.backup_finish_date, (((DATEPART(HH,bus.backup_finish_date))- (DATEPART(HH,bus.backup_start_date)))*3600) + (((DATEPART(MI,bus.backup_finish_date)) - (DATEPART(MI,bus.backup_start_date)))*60) + (((DATEPART(SS,bus.backup_finish_date)) - DATEPART(SS,bus.backup_start_date))) [backup_time (secs)], bus.backup_size, bmf.physical_device_name from backupset bus join backupmediafamily bmf on bus.media_set_id=bmf.media_set_id where bus.backup_start_date >= (getdate() - 7) order by bus.backup_start_date descde
Fig. 5. Eksempel på output af sikkerhedskopiering
Kontrol af sikkerhedskopier på andre forekomster
Ved at bruge linkede servere kan vi udtrække data fra fjerninstanser. I dette tilfælde skal vi bruge en simpel linket server til at udtrække sikkerhedskopieringshistorik fra msdb-databaserne i to fjerninstanser. Sikkerhedskonfigurationen for disse linkede servere afhænger helt af dig, men vi har bevaret den meget enkel her med henblik på vores mål. Liste 3 viser scriptet, der kan bruge disse sammenkædede servere til at samle backuphistorikdata.
Fig. 6. En simpel sammenkædet server
Fig. 7. Sammenkædet server til to fjernforekomster
-- Listing 3: Checking Backups using msdb tables across Linked Servers use msdb go with srva as ( select bus.server_name instance, bus.database_name,bus.type, case bus.type when 'D' then 'Full' when 'I' then 'Differential' when 'L' then 'Log' end backup_type , bus.backup_start_date, bus.backup_finish_date, (((DATEPART(HH,bus.backup_finish_date))- (DATEPART(HH,bus.backup_start_date)))*3600) + (((DATEPART(MI,bus.backup_finish_date)) - (DATEPART(MI,bus.backup_start_date)))*60) + (((DATEPART(SS,bus.backup_finish_date)) - DATEPART(SS,bus.backup_start_date))) [backup_time (secs)], bus.backup_size, bmf.physical_device_name from backupset bus join backupmediafamily bmf on bus.media_set_id=bmf.media_set_id where bus.backup_start_date >= (getdate() - 3) ) , srvb as ( select bus.server_name instance, bus.database_name,bus.type, case bus.type when 'D' then 'Full' when 'I' then 'Differential' when 'L' then 'Log' end backup_type , bus.backup_start_date, bus.backup_finish_date, (((DATEPART(HH,bus.backup_finish_date))- (DATEPART(HH,bus.backup_start_date)))*3600) + (((DATEPART(MI,bus.backup_finish_date)) - (DATEPART(MI,bus.backup_start_date)))*60) + (((DATEPART(SS,bus.backup_finish_date)) - DATEPART(SS,bus.backup_start_date))) [backup_time (secs)], bus.backup_size, bmf.physical_device_name from [10.0.1.155].msdb.dbo.backupset bus join [10.0.1.155].msdb.dbo.backupmediafamily bmf on bus.media_set_id=bmf.media_set_id where bus.backup_start_date >= (getdate() - 3) ) , srvc as ( select bus.server_name instance, bus.database_name,bus.type, case bus.type when 'D' then 'Full' when 'I' then 'Differential' when 'L' then 'Log' end backup_type , bus.backup_start_date, bus.backup_finish_date, (((DATEPART(HH,bus.backup_finish_date))- (DATEPART(HH,bus.backup_start_date)))*3600) + (((DATEPART(MI,bus.backup_finish_date)) - (DATEPART(MI,bus.backup_start_date)))*60) + (((DATEPART(SS,bus.backup_finish_date)) - DATEPART(SS,bus.backup_start_date))) [backup_time (secs)], bus.backup_size, bmf.physical_device_name from [10.0.1.83].msdb.dbo.backupset bus join [10.0.1.83].msdb.dbo.backupmediafamily bmf on bus.media_set_id=bmf.media_set_id where bus.backup_start_date >= (getdate() - 3) ) select * from srva union select * from srvb union select * from srvc;
Integration af SES og Database Mail
Det næste skridt, vi tager, er at automatisere denne kontrol og sende resultatsættet til databaseadministratorer. De nødvendige trin vil være som følger i opsummering:
-
- Konfigurer Amazon SES . Du kan lære, hvordan du hurtigt opsætter e-mail på AWS ved hjælp af dokumentationen fra Amazon SES Quick Start. Når du bruger den lokale e-mail-tjeneste, vil dette ikke være nødvendigt for DBA.
- Konfigurer databasemail . Denne artikel er ikke beregnet til at demonstrere Database Mail, så vi giver bare et skærmbillede af SQL-mail-kontokonfigurationen:
Fig. 7. Indstillinger for SQL Mail-konto
- Portnummeret, når du bruger SES til at sende e-mails, er 587 IKKE 25
- Amazon SES kræver en sikker forbindelse, så afkrydsningsfeltet, der er identificeret med lilla (fig. 7), skal være markeret
- Grundlæggende godkendelse ved hjælp af SMTP-legitimationsoplysninger er påkrævet (dvs. anonym godkendelse er ikke tilladt).
Vi skal bare være opmærksomme på et par ting, når vi bruger Amazon SES til Database Mail:
- Konfigurer SQL Agent til at bruge Mail-profilen . SQL Server Agent skal konfigureres til at bruge den mailprofil, der blev oprettet under Database Mail-konfigurationen, for at agentjobbene kan udløse e-mails. (Se fig. 8)
- Opret en iscenesættelsestabel . En iscenesættelsestabel vil indeholde det aggregerede resultatsæt for alle sikkerhedskopieringshistorikdata fra de forekomster, vi har målrettet mod ved hjælp af linkede servere. Tabellen DDL er vist i liste 4.
-- Listing 4: Backup History Table DDL SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[backuphistory]( [instance] [nvarchar](128) NULL, [database_name] [nvarchar](128) NULL, [type] [char](1) NULL, [backup_type] [varchar](12) NULL, [backup_start_date] [datetime] NULL, [backup_finish_date] [datetime] NULL, [backup_time (secs)] [int] NULL, [backup_size] [numeric](20, 0) NULL, [physical_device_name] [nvarchar](260) NULL ) ON [PRIMARY] GO
Fig. 8. SQL Agent-indstillinger
Vi går videre og planlægger scriptet i liste 3 i et SQL Agent-job, og vi har det komplette script i liste 5.
-- Listing 5: Complete SQL Agent Job for Backup History Notification USE [msdb] GO /****** Object: Job [Enteprise Backup History Summary] Script Date: 9/26/2018 10:16:46 PM ******/ BEGIN TRANSACTION DECLARE @ReturnCode INT SELECT @ReturnCode = 0 /****** Object: JobCategory [[Uncategorized (Local)]] Script Date: 9/26/2018 10:16:46 PM ******/ IF NOT EXISTS (SELECT name FROM msdb.dbo.syscategories WHERE name=N'[Uncategorized (Local)]' AND category_class=1) BEGIN EXEC @ReturnCode = msdb.dbo.sp_add_category @class=N'JOB', @type=N'LOCAL', @name=N'[Uncategorized (Local)]' IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback END DECLARE @jobId BINARY(16) EXEC @ReturnCode = msdb.dbo.sp_add_job @job_name=N'Enteprise Backup History Summary', @enabled=1, @notify_level_eventlog=0, @notify_level_email=0, @notify_level_netsend=0, @notify_level_page=0, @delete_level=0, @description=N'No description available.', @category_name=N'[Uncategorized (Local)]', @owner_login_name=N'TWENTYTOWERS\Administrator', @job_id = @jobId OUTPUT IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback /****** Object: Step [Aggregate Backup History] Script Date: 9/26/2018 10:16:46 PM ******/ EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @[email protected], @step_name=N'Aggregate Backup History', @step_id=1, @cmdexec_success_code=0, @on_success_action=3, @on_success_step_id=0, @on_fail_action=2, @on_fail_step_id=0, @retry_attempts=0, @retry_interval=0, @os_run_priority=0, @subsystem=N'TSQL', @command=N'-- Check Backups using msdb tables -- -- Across Linked Servers use msdb go truncate table [msdb].[dbo].[backuphistory]; with srva as ( select bus.server_name instance, bus.database_name,bus.type, case bus.type when ''D'' then ''Full'' when ''I'' then ''Differential'' when ''L'' then ''Log'' end backup_type , bus.backup_start_date, bus.backup_finish_date, (((DATEPART(HH,bus.backup_finish_date))- (DATEPART(HH,bus.backup_start_date)))*3600) + (((DATEPART(MI,bus.backup_finish_date)) - (DATEPART(MI,bus.backup_start_date)))*60) + (((DATEPART(SS,bus.backup_finish_date)) - DATEPART(SS,bus.backup_start_date))) [backup_time (secs)], bus.backup_size, bmf.physical_device_name from backupset bus join backupmediafamily bmf on bus.media_set_id=bmf.media_set_id where bus.backup_start_date >= (getdate() - 3) ) , srvb as ( select bus.server_name instance, bus.database_name,bus.type, case bus.type when ''D'' then ''Full'' when ''I'' then ''Differential'' when ''L'' then ''Log'' end backup_type , bus.backup_start_date, bus.backup_finish_date, (((DATEPART(HH,bus.backup_finish_date))- (DATEPART(HH,bus.backup_start_date)))*3600) + (((DATEPART(MI,bus.backup_finish_date)) - (DATEPART(MI,bus.backup_start_date)))*60) + (((DATEPART(SS,bus.backup_finish_date)) - DATEPART(SS,bus.backup_start_date))) [backup_time (secs)], bus.backup_size, bmf.physical_device_name from [10.0.1.155].msdb.dbo.backupset bus join [10.0.1.155].msdb.dbo.backupmediafamily bmf on bus.media_set_id=bmf.media_set_id where bus.backup_start_date >= (getdate() - 3) ) , srvc as ( select bus.server_name instance, bus.database_name,bus.type, case bus.type when ''D'' then ''Full'' when ''I'' then ''Differential'' when ''L'' then ''Log'' end backup_type , bus.backup_start_date, bus.backup_finish_date, (((DATEPART(HH,bus.backup_finish_date))- (DATEPART(HH,bus.backup_start_date)))*3600) + (((DATEPART(MI,bus.backup_finish_date)) - (DATEPART(MI,bus.backup_start_date)))*60) + (((DATEPART(SS,bus.backup_finish_date)) - DATEPART(SS,bus.backup_start_date))) [backup_time (secs)], bus.backup_size, bmf.physical_device_name from [10.0.1.83].msdb.dbo.backupset bus join [10.0.1.83].msdb.dbo.backupmediafamily bmf on bus.media_set_id=bmf.media_set_id where bus.backup_start_date >= (getdate() - 3) ) insert into [msdb].[dbo].[backuphistory] select * from srva union select * from srvb union select * from srvc; ', @database_name=N'msdb', @flags=0 IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback /****** Object: Step [Query Member Servers for Backups] Script Date: 9/26/2018 10:16:46 PM ******/ EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @[email protected], @step_name=N'Query Member Servers for Backups', @step_id=2, @cmdexec_success_code=0, @on_success_action=3, @on_success_step_id=0, @on_fail_action=2, @on_fail_step_id=0, @retry_attempts=0, @retry_interval=0, @os_run_priority=0, @subsystem=N'TSQL', @command=N'DECLARE @tableHTML NVARCHAR(MAX) ; SET @tableHTML = N''<H1><font face="Verdana" size="4">Enterprise Backup History Summary</H1>'' + N''<table border="1"><font face="Verdana" size="2">'' + N''<tr><th><font face="Verdana" size="2">Instance Name</th>'' + N''<th><font face="Verdana" size="2">Database Name</th>'' + N''<th><font face="Verdana" size="2">Backup Start Date</th>'' + N''<th><font face="Verdana" size="2">Backup Finish Date</th>'' + N''<th><font face="Verdana" size="2">Backup Time (secs)</th>'' + N''<th><font face="Verdana" size="2">Backup Size</th>'' + N''<th><font face="Verdana" size="2">Physical Device Name</th></tr>'' + CAST ( ( SELECT td = bus.instance, '''', td = bus.database_name, '''', td = bus.backup_start_date, '''', td = bus.backup_finish_date, '''', td = (((DATEPART(HH,bus.backup_finish_date))- (DATEPART(HH,bus.backup_start_date)))*3600) + (((DATEPART(MI,bus.backup_finish_date)) - (DATEPART(MI,bus.backup_start_date)))*60) + (((DATEPART(SS,bus.backup_finish_date)) - DATEPART(SS,bus.backup_start_date))), '''', td = bus.backup_size, '''', td = bus.physical_device_name FROM backuphistory as bus WHERE bus.backup_start_date >= (getdate() - 7) ORDER BY bus.backup_start_date desc FOR XML PATH(''tr''), TYPE ) AS NVARCHAR(MAX) ) + N''</table>'' + ''<p style="margin-top: 0; margin-bottom: 0"> </p> <p style="margin-top: 0; margin-bottom: 0"><font face="Verdana" size="2">Thanks and Regards,</font></p> <p style="margin-top: 0; margin-bottom: 0"><font face="Verdana" size="2">Enterprise Database Operations</font></p> <p> </p>'' ; EXEC msdb.dbo.sp_send_dbmail @recipients=''[email protected];[email protected]'', @subject = ''Enterprise Backup History Summary'', @body = @tableHTML , @body_format = ''HTML'' ;', @database_name=N'msdb', @flags=0 IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback /****** Object: Step [Mail Complete Result Set to Support] Script Date: 9/26/2018 10:16:46 PM ******/ EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @[email protected], @step_name=N'Mail Complete Result Set to Support', @step_id=3, @cmdexec_success_code=0, @on_success_action=1, @on_success_step_id=0, @on_fail_action=2, @on_fail_step_id=0, @retry_attempts=0, @retry_interval=0, @os_run_priority=0, @subsystem=N'TSQL', @command=N'DECLARE @tableHTML NVARCHAR(MAX) ; SET @tableHTML = N''<H1><font face="Verdana" size="4">Enterprise Backup History Summary</H1>'' + N''<table border="1"><font face="Verdana" size="2">'' + N''<tr><th><font face="Verdana" size="2">Instance Name</th>'' + N''<th><font face="Verdana" size="2">Database Name</th>'' + N''<th><font face="Verdana" size="2">Backup Start Date</th>'' + N''<th><font face="Verdana" size="2">Backup Finish Date</th>'' + N''<th><font face="Verdana" size="2">Backup Time (secs)</th>'' + N''<th><font face="Verdana" size="2">Backup Size</th>'' + N''<th><font face="Verdana" size="2">Physical Device Name</th></tr>'' + CAST ( ( SELECT td = bus.instance, '''', td = bus.database_name, '''', td = bus.backup_start_date, '''', td = bus.backup_finish_date, '''', td = (((DATEPART(HH,bus.backup_finish_date))- (DATEPART(HH,bus.backup_start_date)))*3600) + (((DATEPART(MI,bus.backup_finish_date)) - (DATEPART(MI,bus.backup_start_date)))*60) + (((DATEPART(SS,bus.backup_finish_date)) - DATEPART(SS,bus.backup_start_date))), '''', td = bus.backup_size, '''', td = bus.physical_device_name FROM backuphistory as bus WHERE bus.backup_start_date >= (getdate() - 7) ORDER BY bus.backup_start_date desc FOR XML PATH(''tr''), TYPE ) AS NVARCHAR(MAX) ) + N''</table>'' + ''<p style="margin-top: 0; margin-bottom: 0"> </p> <p style="margin-top: 0; margin-bottom: 0"><font face="Verdana" size="2">Thanks and Regards,</font></p> <p style="margin-top: 0; margin-bottom: 0"><font face="Verdana" size="2">Enterprise Database Operations</font></p> <p> </p>'' ; EXEC msdb.dbo.sp_send_dbmail @recipients=''[email protected];[email protected]'', @subject = ''Enterprise Backup History Summary'', @body = @tableHTML , @body_format = ''HTML'' ;', @database_name=N'msdb', @flags=0 IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback EXEC @ReturnCode = msdb.dbo.sp_update_job @job_id = @jobId, @start_step_id = 1 IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback EXEC @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @jobId, @server_name = N'(local)' IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback COMMIT TRANSACTION GOTO EndSave QuitWithRollback: IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION EndSave: GO
Udførelse af dette job resulterer i output vist i Fig. 9. Tabellen er oprettet ved hjælp af meget simpel HTML og kan videreudvikles, så den passer til dine behov.
Fig. 9. E-mail-output af SQL Agent-jobudførelse
Konklusion
Vi har gennemgået en simpel metode til at aggregere sikkerhedskopieringshistorikoplysninger (og eventuelt andre data indeholdt i systemdatabaser) ved hjælp af linkede servere. Vi fortsatte med at automatisere denne proces ved hjælp af SQL Agent, Database Mail og lidt HTML. Denne metode kan virke lidt rå, og jeg er sikker på, at der er værktøjer derude, som kan gøre det meget bedre, men dette ville tjene til serverformål for dem, der lige er startet med SQL Server eller miljøer på et lavt budget. Med en lille smule kreativitet kan du yderligere tilpasse scripts og tilpasse scripts til andre formål.
Referencer
- Konfiguration af databasemail
- Kom godt i gang med Amazon SES
- Linkede servere
- Sikkerhedskopieringshistorik og headeroplysninger