sql >> Database teknologi >  >> RDS >> Database

Overvågning af sikkerhedskopier på tværs af instanser

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å:

  1. Brug af linkede servere, katalogvisninger, SQL Agent-job og databasemail
  2. 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 desc
de

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:

    1. 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.
    2. 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:

    3. 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)
    4. 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">&nbsp;</p>
	<p style="margin-top: 0; margin-bottom: 0"><font face="Verdana" size="2">Thanks   
	and Regards,</font></p>  &nbsp;
	<p style="margin-top: 0; margin-bottom: 0"><font face="Verdana" size="2">Enterprise Database Operations</font></p>  
	<p>&nbsp;</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">&nbsp;</p>
	<p style="margin-top: 0; margin-bottom: 0"><font face="Verdana" size="2">Thanks   
	and Regards,</font></p>  &nbsp;
	<p style="margin-top: 0; margin-bottom: 0"><font face="Verdana" size="2">Enterprise Database Operations</font></p>  
	<p>&nbsp;</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

  1. Konfiguration af databasemail
  2. Kom godt i gang med Amazon SES
  3. Linkede servere
  4. Sikkerhedskopieringshistorik og headeroplysninger

  1. MySQL-fejl #1071 - Den angivne nøgle var for lang; max nøglelængde er 767 bytes

  2. Hvordan opretter man json-format med group-concat mysql?

  3. SQL Server erstat, fjern alt efter et bestemt tegn

  4. Kan jeg bede Postgresql om at ignorere fejl i en transaktion