Introduzione
Negli ultimi due o tre mesi mi è stata richiesta due volte una soluzione nativa per SQL Server che consolidi un report di backup per diverse istanze di SQL Server in un'azienda. Questa domanda proveniva da amici che non volevano necessariamente spendere soldi per acquistare uno strumento, ma erano più inclini a sfruttare le capacità di SQL Server. Ho pensato a due possibili modi per raggiungere questo obiettivo:
- Utilizzo di server collegati, viste catalogo, processi di SQL Agent e posta database
- Utilizzo del server di gestione centrale
In questo articolo dimostrerò il primo e spero di avere una seconda parte dell'articolo qualche tempo dopo.
Scenario
Il mio ambiente è costituito da un set di tre istanze che si trovano su server separati su AWS. Questi "server" sono in realtà Amazon EC2 che eseguono SQL Server 2017 RTM CU5. Sfrutteremo anche Amazon Simple Email Service per configurare la posta del database. In produzione, puoi sicuramente utilizzare i tuoi server di posta elettronica locali e raggiungere gli stessi obiettivi. Noterai più avanti in questo articolo che il nome host (e quindi i nomi delle istanze) sono gli stessi. Questo perché i server sono stati clonati dalla stessa Amazon Machine Image (scusate la "pigrizia"). Questo non sarà probabilmente il caso in produzione.
Fai alcuni backup
Iniziamo eseguendo alcuni backup dei database che si trovano su queste tre istanze. Questo genererà i dati con cui lavoreremo. Verificheremo quindi che i backup siano acquisiti nelle tabelle di sistema msdb.dbo.backupset e msdb.dbo.backupmediafamily . Le descrizioni complete di queste tabelle possono essere esaminate su questa documentazione Microsoft o semplicemente utilizzando 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. Descrizione di msdb.dbo.backupset
Controllo dei backup
Lo script seguente sfrutta due viste catalogo backupset e backupmediafamily per esaminare la cronologia dei backup creati su un'istanza di SQL Server. Il catalogo del set di backup contiene una riga per ogni set di backup. Un set di backup è definito come il contenuto di un'operazione di backup aggiunta a un set di supporti. Un set di supporti è una raccolta di ordini di supporti su cui sono state scritte una o più operazioni di backup.
-- 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 desc
Fig 5. Esempio di output dei controlli di backup
Verifica dei backup su altre istanze
Utilizzando i server collegati, possiamo estrarre dati da istanze remote. In questo caso, utilizzeremo un semplice server collegato per estrarre le informazioni sulla cronologia dei backup dai database msdb di due istanze remote. La configurazione della sicurezza per questi server collegati dipende totalmente da te, ma abbiamo mantenuto è molto semplice qui per lo scopo del nostro obiettivo. Il Listato 3 mostra lo script che può utilizzare questi server collegati per aggregare i dati della cronologia dei backup.
Fig 6. Un semplice server collegato
Fig 7. Server collegato per due istanze remote
-- 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;
Incorporando SES e Database Mail
Il prossimo passo che facciamo è automatizzare questo controllo e inviare il set di risultati agli amministratori del database. I passaggi necessari sarebbero i seguenti in sintesi:
-
- Configura Amazon SES . Puoi scoprire come configurare rapidamente l'e-mail su AWS utilizzando la documentazione fornita in Amazon SES Quick Start. Quando si utilizza il servizio di posta elettronica in sede, questo non sarà necessario per il DBA.
- Configura Posta Database . Questo articolo non è destinato a una dimostrazione di Posta elettronica database, quindi forniamo solo uno screenshot della configurazione dell'account di posta SQL:
Fig 7. Impostazioni dell'account di posta SQL
- Il numero di porta quando si utilizza SES per inviare e-mail è 587 NON 25
- Amazon SES richiede una connessione sicura, quindi è necessario selezionare la casella di controllo identificata in lilla (Fig. 7)
- È richiesta l'autenticazione di base tramite le credenziali SMTP (ovvero non è consentita l'autenticazione anonima).
Dobbiamo solo essere consapevoli di alcune cose quando si utilizza Amazon SES per Database Mail:
- Configura SQL Agent per utilizzare il profilo di posta . SQL Server Agent deve essere configurato per utilizzare il profilo di posta creato durante la configurazione di Posta elettronica database affinché i processi dell'agente inviino messaggi di posta elettronica. (Vedi Fig. 8)
- Crea una tabella di staging . Una tabella di staging conterrà il set di risultati aggregati per tutti i dati della cronologia di backup dalle istanze che abbiamo scelto come target utilizzando i server collegati. La tabella DDL è mostrata nel Listato 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. Impostazioni dell'agente SQL
Andiamo avanti e pianifichiamo lo script nel listato 3 in un processo di SQL Agent e abbiamo lo script completo nel listato 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
L'esecuzione di questo lavoro produce l'output mostrato in Fig 9. La tabella viene creata utilizzando un HTML molto semplice e può essere ulteriormente sviluppata per soddisfare le tue esigenze.
Fig 9. Output e-mail dell'esecuzione del processo di SQL Agent
Conclusione
Abbiamo utilizzato un metodo semplice per aggregare le informazioni sulla cronologia dei backup (ed eventuali altri dati contenuti nei database di sistema) utilizzando server collegati. Abbiamo ulteriormente automatizzato questo processo utilizzando SQL Agent, Database Mail e un po' di HTML. Questo metodo può sembrare un po' grezzo e sono sicuro che ci sono strumenti là fuori che possono fare molto meglio, ma questo sarebbe lo scopo del server per coloro che hanno appena iniziato con SQL Server o ambienti con un budget ridotto. Con un po' di creatività, puoi personalizzare ulteriormente gli script e adattarli ad altri usi.
Riferimenti
- Configurazione della posta database
- Guida introduttiva ad Amazon SES
- Server collegati
- Cronologia di backup e informazioni sull'intestazione