Inleiding
In de afgelopen twee of drie maanden ben ik twee keer gevraagd voor een native SQL Server-oplossing die een back-uprapport consolideert voor verschillende SQL Server-instanties in een onderneming. Deze vraag kwam van vrienden die niet per se geld wilden uitgeven aan het kopen van een tool, maar meer geneigd waren om gebruik te maken van de mogelijkheden van SQL Server. Ik heb twee mogelijke manieren bedacht om dit te bereiken:
- Gekoppelde servers, catalogusweergaven, SQL Agent-taken en databasemail gebruiken
- Central Management Server gebruiken
In dit artikel zal ik het eerste demonstreren en ik hoop dat we later een tweede deel van het artikel zullen hebben.
Scenario
Mijn omgeving bestaat uit een set van drie instanties die op afzonderlijke servers op AWS staan. Deze "servers" zijn eigenlijk Amazon EC2's met SQL Server 2017 RTM CU5. We gaan ook profiteren van Amazon Simple Email Service om Database Mail te configureren. In productie kunt u zeker uw on-premise e-mailservers gebruiken en dezelfde doelen bereiken. U zult later in dit artikel opmerken dat de hostnaam (en dus de instantienamen) hetzelfde zijn. Dit komt omdat de servers zijn gekloond van dezelfde Amazon Machine Image (excuseer de "luiheid"). Dit zal waarschijnlijk niet het geval zijn in productie.
Maak een paar back-ups
Laten we beginnen met het maken van een paar back-ups van databases op deze drie instanties. Dit genereert de gegevens waarmee we gaan werken. We zullen dan controleren of de back-ups zijn vastgelegd in de systeemtabellen msdb.dbo.backupset en msdb.dbo.backupmediafamily . De volledige beschrijvingen van deze tabellen kunnen worden bekeken in deze Microsoft-documentatie of u kunt gewoon sp_columns gebruiken .
-- 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. Beschrijving van msdb.dbo.backupset
Back-ups controleren
Het volgende script maakt gebruik van twee catalogusweergaven backupset en backupmediafamily om de geschiedenis te onderzoeken van back-ups die zijn gemaakt op een exemplaar van SQL Server. De backupset-catalogus bevat een rij voor elke back-upset. Een back-upset wordt gedefinieerd als de inhoud van een back-upbewerking die aan een mediaset wordt toegevoegd. Een mediaset is een orderverzameling van media waarnaar een of meer back-upbewerkingen zijn geschreven.
-- 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. Voorbeelduitvoer van back-upcontroles
Back-ups controleren op andere instanties
Met behulp van Linked Servers kunnen we gegevens van externe instanties extraheren. In dit geval zullen we een eenvoudige gekoppelde server gebruiken om informatie over de back-upgeschiedenis te extraheren uit de msdb-databases van twee externe instanties. De beveiligingsconfiguratie voor deze gekoppelde servers hangt volledig van u af, maar we hebben het hier heel eenvoudig gehouden voor ons doel. Lijst 3 toont het script dat deze gekoppelde servers kan gebruiken om back-upgeschiedenisgegevens te verzamelen.
Fig 6. Een eenvoudige gekoppelde server
Fig 7. Gekoppelde server voor twee externe instanties
-- 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;
SES en databasemail opnemen
De volgende stap die we nemen is om deze controle te automatiseren en de resultatenset naar Database Administrators te mailen. De vereiste stappen zijn samengevat als volgt:
-
- Amazon SES configureren . U kunt leren hoe u snel e-mail op AWS kunt instellen met behulp van de documentatie bij Amazon SES Quick Start. Bij gebruik van een lokale e-mailservice is dit niet nodig voor de DBA.
- Database-e-mail configureren . Dit artikel is niet bedoeld om Database Mail te demonstreren, dus we geven alleen een screenshot van de SQL-mailaccountconfiguratie:
Fig 7. Instellingen SQL Mail-account
- Het poortnummer bij gebruik van SES om e-mails te verzenden is 587 NIET 25
- Amazon SES vereist een beveiligde verbinding, dus het selectievakje in lila (Fig. 7) moet worden geselecteerd
- Basisverificatie met behulp van de SMTP-inloggegevens is vereist (d.w.z. anonieme authenticatie is niet toegestaan).
We moeten ons alleen bewust zijn van een paar dingen bij het gebruik van Amazon SES voor Database Mail:
- SQL Agent configureren om het e-mailprofiel te gebruiken . SQL Server Agent moet worden geconfigureerd om het e-mailprofiel te gebruiken dat is gemaakt tijdens de configuratie van Database Mail, zodat de agenttaken e-mails kunnen afvuren. (Zie Afb. 8)
- Maak een verzameltabel . Een verzameltabel bevat de geaggregeerde resultatenset voor alle back-upgeschiedenisgegevens van de instanties die we hebben getarget met behulp van gekoppelde servers. De tabel DDL wordt weergegeven in Listing 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-instellingen
We gaan door en plannen het script in listing 3 in een SQL Agent Job en we hebben het volledige script in Listing 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
Het uitvoeren van deze taak resulteert in de uitvoer die wordt getoond in figuur 9. De tabel is gemaakt met behulp van zeer eenvoudige HTML en kan verder worden ontwikkeld om aan uw behoeften te voldoen.
Fig 9. E-mailuitvoer van uitvoering van SQL Agent-taak
Conclusie
We hebben een eenvoudige methode doorlopen om back-upgeschiedenisgegevens (en mogelijk andere gegevens in systeemdatabases) samen te voegen met behulp van gekoppelde servers. We gingen verder met het automatiseren van dit proces met behulp van SQL Agent, Database Mail en een beetje HTML. Deze methode lijkt misschien een beetje grof en ik weet zeker dat er tools zijn die veel beter kunnen, maar dit zou serverdoel zijn voor degenen die net beginnen met SQL Server of omgevingen met een laag budget. Met een beetje creativiteit kun je de scripts verder aanpassen en aanpassen aan ander gebruik.
Referenties
- Database-e-mail configureren
- Aan de slag met Amazon SES
- Gelinkte servers
- Back-upgeschiedenis en koptekstinformatie