Database
 sql >> Base de Dados >  >> RDS >> Database

Monitoramento de backups em instâncias

Introdução


Nos últimos dois ou três meses, fui solicitado duas vezes por uma solução nativa do SQL Server que consolida um relatório de backup para várias instâncias do SQL Server em uma empresa. Essa pergunta veio de amigos que não necessariamente queriam gastar dinheiro comprando uma ferramenta, mas estavam mais inclinados a aproveitar os recursos do SQL Server. Eu pensei em duas maneiras possíveis de conseguir isso:
  1. Usando servidores vinculados, exibições de catálogo, trabalhos do SQL Agent e Database Mail
  2. Usando o servidor de gerenciamento central

Neste artigo, demonstrarei o primeiro e espero que tenhamos uma segunda parte do artigo algum tempo depois.

Cenário


Meu ambiente consiste em um conjunto de três instâncias em servidores separados na AWS. Esses “servidores” são, na verdade, Amazon EC2s executando o SQL Server 2017 RTM CU5. Também vamos aproveitar o Amazon Simple Email Service para configurar o Database Mail. Na produção, você pode definitivamente usar seus servidores de e-mail locais e atingir os mesmos objetivos. Você notará posteriormente neste artigo que o nome do host (e, portanto, os nomes das instâncias) são os mesmos. Isso porque os servidores foram clonados da mesma Amazon Machine Image (desculpe a “preguiça”). Isso provavelmente não será o caso na produção.

Faça alguns backups


Vamos começar fazendo alguns backups de bancos de dados nessas três instâncias. Isso gerará os dados com os quais trabalharemos. Devemos então verificar se os backups são capturados nas tabelas do sistema msdb.dbo.backupset e msdb.dbo.backupmediafamily . As descrições completas dessas tabelas podem ser revisadas nesta Documentação da Microsoft ou simplesmente usando 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. Descrevendo msdb.dbo.backupset

Verificando backups


O script a seguir tira proveito de duas visualizações de catálogo backupset e backupmediafamily para examinar o histórico de backups criados em uma instância do SQL Server. O catálogo do conjunto de backup contém uma linha para cada conjunto de backup. Um conjunto de backup é definido como o conteúdo de uma operação de backup que é adicionada a um conjunto de mídia. Um conjunto de mídia é uma coleção ordenada de mídia na qual uma ou mais operações de backup foram gravadas.
-- 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. Exemplo de saída de verificações de backup

Verificando backups em outras instâncias


Usando servidores vinculados, podemos extrair dados de instâncias remotas. Nesse caso, usaremos um servidor vinculado simples para extrair as informações do histórico de backup dos bancos de dados msdb de duas instâncias remotas. A configuração de segurança para esses servidores vinculados depende totalmente de você, mas mantivemos aqui muito simples para o propósito de nosso objetivo. A Listagem 3 mostra o script que pode utilizar esses servidores vinculados para agregar dados do histórico de backup.

Fig 6. Um Servidor Vinculado Simples



Fig 7. Servidor Vinculado para Duas Instâncias Remotas
-- 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


A próxima etapa que damos é automatizar essa verificação e enviar o conjunto de resultados para os administradores de banco de dados. As etapas necessárias seriam as seguintes em resumo:
    1. Configurar o Amazon SES . Você pode aprender como configurar rapidamente o e-mail na AWS usando a documentação fornecida no Amazon SES Quick Start. Ao usar o serviço de e-mail local, isso não será necessário para o DBA.
    2. Configurar o Database Mail . Este artigo não se destina à demonstração do Database Mail, portanto, apenas fornecemos uma captura de tela da configuração da conta de email do SQL:
      Fig 7. Configurações de Conta de Correio SQL
      • O número da porta ao usar SES para enviar e-mails é 587 NOT 25
      • O Amazon SES requer uma conexão segura, portanto, a caixa de seleção identificada em lilás (Fig. 7) deve ser selecionada
      • A autenticação básica usando as credenciais SMTP é necessária (ou seja, a autenticação anônima não é permitida).

      Só precisamos estar cientes de algumas coisas ao usar o Amazon SES para Database Mail:
    3. Configure o SQL Agent para usar o perfil de email . O SQL Server Agent deve ser configurado para usar o perfil de email criado durante a configuração do Database Mail para que os trabalhos do agente disparem emails. (Veja a Fig. 8)
    4. Criar uma tabela de preparo . Uma tabela de preparo conterá o conjunto de resultados agregados para todos os dados do histórico de backup das instâncias que direcionamos usando Servidores Vinculados. A tabela DDL é mostrada na Listagem 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. Configurações do SQL Agent

Vamos em frente e agendamos o script na listagem 3 em um SQL Agent Job e temos o script completo na Listagem 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

A execução deste trabalho resulta na saída mostrada na Fig 9. A tabela é criada usando HTML muito simples e pode ser desenvolvida para atender às suas necessidades.

Fig 9. Saída de e-mail da execução do trabalho do SQL Agent

Conclusão


Passamos por um método simples de agregar informações de histórico de backup (e possivelmente quaisquer outros dados contidos em bancos de dados do sistema) usando servidores vinculados. Continuamos automatizando esse processo usando SQL Agent, Database Mail e um pouco de HTML. Esse método pode parecer um pouco grosseiro e tenho certeza de que existem ferramentas que podem fazer muito melhor, mas isso seria um objetivo do servidor para quem está começando com o SQL Server ou ambientes com baixo orçamento. Com um pouco de criatividade, você pode personalizar ainda mais os scripts e adaptá-los para outros usos.

Referências

  1. Configurando o Database Mail
  2. Introdução ao Amazon SES
  3. Servidores vinculados
  4. Histórico de backup e informações de cabeçalho