Database
 sql >> Baza danych >  >> RDS >> Database

Monitorowanie kopii zapasowych w różnych instancjach

Wprowadzenie

W ciągu ostatnich dwóch lub trzech miesięcy dwukrotnie poproszono mnie o rozwiązanie natywne dla SQL Server, które konsoliduje raport kopii zapasowych dla kilku instancji SQL Server w przedsiębiorstwie. To pytanie zadali znajomi, którzy niekoniecznie chcieli wydawać pieniądze na zakup narzędzia, ale byli bardziej skłonni do korzystania z możliwości SQL Server. Myślałem o dwóch możliwych sposobach osiągnięcia tego celu:

  1. Korzystanie z połączonych serwerów, widoków katalogu, zadań SQL Agent i poczty bazy danych
  2. Korzystanie z Centralnego Serwera Zarządzania

W tym artykule zademonstruję pierwszą część i mam nadzieję, że wkrótce będziemy mieli drugą część artykułu.

Scenariusz

Moje środowisko składa się z zestawu trzech instancji umieszczonych na osobnych serwerach na AWS. Te „serwery” to w rzeczywistości Amazon EC2 z systemem SQL Server 2017 RTM CU5. Będziemy również korzystać z usługi Amazon Simple Email Service, aby skonfigurować pocztę bazy danych. W środowisku produkcyjnym z pewnością możesz korzystać z lokalnych serwerów pocztowych i osiągać te same cele. W dalszej części tego artykułu zauważysz, że nazwa hosta (a tym samym nazwy instancji) są takie same. Dzieje się tak, ponieważ serwery zostały sklonowane z tego samego obrazu Amazon Machine (przepraszam za „lenistwo”). To prawdopodobnie nie będzie miało miejsca w przypadku produkcji.

Zrób kilka kopii zapasowych

Zacznijmy od wykonania kilku kopii zapasowych baz danych znajdujących się na tych trzech instancjach. To wygeneruje dane, z którymi będziemy pracować. Następnie sprawdzimy, czy kopie zapasowe są przechwycone w tabelach systemowych msdb.dbo.backupset i msdb.dbo.backupmediafamily . Pełne opisy tych tabel można przejrzeć w tej dokumentacji Microsoft lub po prostu użyć 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

Rys 3. Opis msdb.dbo.backupset

Sprawdzanie kopii zapasowych

Poniższy skrypt korzysta z dwóch widoków katalogu zestawu kopii zapasowych i backupmediafamily do zbadania historii kopii zapasowych utworzonych na instancji SQL Server. Katalog zestawów kopii zapasowych zawiera wiersz dla każdego zestawu kopii zapasowych. Zestaw kopii zapasowej jest definiowany jako zawartość operacji tworzenia kopii zapasowej dodawana do zestawu nośników. Zestaw nośników to kolejna kolekcja nośników, na której zapisano jedną lub więcej operacji tworzenia kopii zapasowych.

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

Rys 5. Przykładowe wyjście kontroli kopii zapasowych

Sprawdzanie kopii zapasowych w innych instancjach

Korzystając z Linked Servers, możemy wyodrębnić dane ze zdalnych instancji. W tym przypadku użyjemy prostego serwera połączonego, aby wyodrębnić informacje o historii kopii zapasowych z baz danych msdb dwóch zdalnych instancji. Konfiguracja bezpieczeństwa dla tych serwerów połączonych całkowicie zależy od Ciebie, ale zachowaliśmy bardzo prostą dla celów naszego celu. Listing 3 pokazuje skrypt, który może wykorzystywać te serwery połączone do agregowania danych historii kopii zapasowych.

Rys 6. Prosty serwer połączony

Rys 7. Połączony serwer dla dwóch zdalnych instancji

-- 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;

Włączanie SES i poczty bazy danych

Następnym krokiem, jaki podejmujemy, jest zautomatyzowanie tego sprawdzenia i wysłanie zestawu wyników do administratorów bazy danych. Wymagane kroki są następujące w podsumowaniu:

    1. Skonfiguruj Amazon SES . Możesz dowiedzieć się, jak szybko skonfigurować pocztę e-mail w AWS, korzystając z dokumentacji dostarczonej w Amazon SES Quick Start. W przypadku korzystania z lokalnej usługi poczty e-mail nie będzie to konieczne w przypadku DBA.
    2. Konfiguruj pocztę bazy danych . Ten artykuł nie jest przeznaczony do demonstracji poczty bazy danych, więc po prostu przedstawiamy zrzut ekranu konfiguracji konta pocztowego SQL:

      Rys 7. Ustawienia konta pocztowego SQL

      • Numer portu podczas korzystania z SES do wysyłania e-maili to 587, a nie 25
      • Amazon SES wymaga bezpiecznego połączenia, dlatego pole wyboru oznaczone kolorem liliowym (ryc. 7) musi być zaznaczone
      • Wymagane jest podstawowe uwierzytelnianie przy użyciu poświadczeń SMTP (tj. uwierzytelnianie anonimowe jest niedozwolone).

      Podczas korzystania z Amazon SES dla poczty bazy danych musimy tylko pamiętać o kilku rzeczach:

    3. Skonfiguruj agenta SQL do korzystania z profilu poczty . Agent programu SQL Server musi być skonfigurowany do korzystania z profilu poczty utworzonego podczas konfiguracji poczty bazy danych, aby zadania agenta uruchamiały wiadomości e-mail. (patrz rys. 8)
    4. Utwórz tabelę pomostową . Tabela pomostowa będzie zawierać zagregowany zestaw wyników dla wszystkich danych historii kopii zapasowych z instancji, na które nakierowaliśmy za pomocą połączonych serwerów. Tabela DDL jest pokazana na Listingu 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

Rys 8. Ustawienia agenta SQL

Kontynuujemy i planujemy skrypt na liście 3 w zadaniu agenta SQL, a kompletny skrypt mamy na liście 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

Wykonanie tego zadania daje wynik pokazany na Rys. 9. Tabela jest tworzona przy użyciu bardzo prostego kodu HTML i może być dalej rozwijana w celu dostosowania do Twoich potrzeb.

Rys 9. Dane wyjściowe e-maili wykonywania zadań SQL Agent

Wniosek

Przeszliśmy przez prostą metodę agregowania informacji o historii kopii zapasowych (i ewentualnie innych danych zawartych w bazach danych systemu) za pomocą serwerów połączonych. Następnie zautomatyzowaliśmy ten proces za pomocą agenta SQL, poczty bazy danych i niewielkiego kodu HTML. Ta metoda może wydawać się nieco prymitywna i jestem pewien, że istnieją narzędzia, które mogą działać znacznie lepiej, ale byłoby to przeznaczenie serwerowe dla tych, którzy dopiero zaczynają korzystać z SQL Server lub środowisk o niskim budżecie. Przy odrobinie kreatywności możesz dalej dostosowywać skrypty i dostosowywać je do innych zastosowań.

Referencje

  1. Konfigurowanie poczty bazy danych
  2. Pierwsze kroki z Amazon SES
  3. Serwery połączone
  4. Historia kopii zapasowej i informacje nagłówka

  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Odkrywanie testów jednostkowych Java za pomocą JUnit Test Framework

  2. Czy ColumnStore może pomóc w obciążeniu stronicowaniem?

  3. Radzenie sobie z datą i godziną zamiast datetime

  4. Reguły Codda w SQL

  5. 2013 MVP Summit:krótki przegląd i spojrzenie w przód