はじめに
過去2、3か月の間に、企業全体の複数のSQLServerインスタンスのバックアップレポートを統合するSQLServerネイティブのソリューションを2回求められました。この質問は、必ずしもツールの購入にお金をかけたくないが、SQLServerの機能を活用する傾向がある友人から寄せられました。これを達成するための2つの可能な方法について考えました:
- リンクサーバー、カタログビュー、SQLエージェントジョブおよびデータベースメールの使用
- 中央管理サーバーの使用
この記事では、最初の部分を示し、後で記事の2番目の部分があることを願っています。
シナリオ
私の環境は、AWSの別々のサーバーにある3つのインスタンスのセットで構成されています。これらの「サーバー」は、実際にはSQL Server 2017RTMCU5を実行しているAmazonEC2です。また、Amazon SimpleEmailServiceを利用してデータベースメールを構成します。本番環境では、オンプレミスの電子メールサーバーを確実に使用して、同じ目標を達成できます。この記事の後半で、ホスト名(したがってインスタンス名)が同じであることに気付くでしょう。これは、サーバーが同じAmazonマシンイメージからクローン化されたためです(「怠惰」はご容赦ください)。これは、本番環境では当てはまらない可能性があります。
いくつかのバックアップを取る
これらの3つのインスタンスにあるデータベースのバックアップをいくつか作成することから始めましょう。これにより、処理するデータが生成されます。次に、バックアップがシステムテーブル msdb.dbo.backupsetにキャプチャされていることを確認します。 およびmsdb.dbo.backupmediafamily 。これらの表の完全な説明は、このMicrosoftドキュメントで確認するか、 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 example@sqldat.com' -- 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
図3.msdb.dbo.backupsetの説明
バックアップの確認
次のスクリプトは、2つのカタログビューbackupsetと backupmediafamily を利用しています。 SQLServerのインスタンスで作成されたバックアップの履歴を調べるため。バックアップセットカタログには、各バックアップセットの行が含まれています。バックアップセットは、メディアセットに追加されるバックアップ操作の内容として定義されます。メディアセットは、1つ以上のバックアップ操作が書き込まれたメディアの注文コレクションです。
-- 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
図5.バックアップチェックの出力例
他のインスタンスのバックアップを確認する
リンクサーバーを使用すると、リモートインスタンスからデータを抽出できます。この場合、単純なリンクサーバーを使用して、2つのリモートインスタンスのmsdbデータベースからバックアップ履歴情報を抽出します。これらのリンクサーバーのセキュリティ構成は完全にあなた次第ですが、私たちがここで維持しているのは、私たちの目的のために非常に単純です。リスト3は、これらのリンクサーバーを利用してバックアップ履歴データを集約できるスクリプトを示しています。
図6.単純なリンクサーバー
図7.2つのリモートインスタンスのリンクサーバー
-- 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とデータベースメールの組み込み
次のステップは、このチェックを自動化し、結果セットをデータベース管理者にメールで送信することです。必要な手順の概要は次のとおりです。
-
- AmazonSESを設定する 。 Amazon SESクイックスタートで提供されているドキュメントを使用して、AWSでメールをすばやく設定する方法を学ぶことができます。オンプレミスの電子メールサービスを使用する場合、これはDBAには必要ありません。
- データベースメールの構成 。この記事はデータベースメールのデモンストレーションを目的としたものではないため、SQLメールアカウント構成のスクリーンショットを示します。
図7.SQLメールアカウントの設定
- SESを使用してメールを送信するときのポート番号は25ではなく587です
- Amazon SESには安全な接続が必要であるため、ライラックで識別されるチェックボックス(図7)を選択する必要があります
- SMTP認証情報を使用した基本認証が必要です(つまり、匿名認証は許可されていません)。
データベースメールにAmazonSESを使用する場合は、次の点に注意する必要があります。
- メールプロファイルを使用するようにSQLエージェントを構成する 。 SQL Serverエージェントは、データベースメールの構成中に作成されたメールプロファイルを使用して、エージェントジョブが電子メールを送信するように構成する必要があります。 (図8を参照)
- ステージングテーブルを作成する 。ステージングテーブルには、リンクサーバーを使用してターゲットにしたインスタンスからのすべてのバックアップ履歴データの集計結果セットが含まれます。テーブルDDLをリスト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
図8.SQLエージェントの設定
先に進み、SQLエージェントジョブのリスト3のスクリプトをスケジュールします。リスト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 @example@sqldat.com, @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 @example@sqldat.com, @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=''example@sqldat.com;example@sqldat.com'',
@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 @example@sqldat.com, @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=''example@sqldat.com;example@sqldat.com'',
@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 このジョブを実行すると、図9に示す出力が得られます。テーブルは非常に単純なHTMLを使用して作成され、ニーズに合わせてさらに開発できます。
図9.SQLエージェントジョブ実行の電子メール出力
結論
リンクサーバーを使用して、バックアップ履歴情報(およびシステムデータベースに含まれるその他のデータ)を集約する簡単な方法を実行しました。さらに、SQLエージェント、データベースメール、および小さなHTMLを使用して、このプロセスを自動化しました。この方法は少し粗雑に思えるかもしれませんし、もっとうまくいくツールがあると確信していますが、これはSQLServerや低予算の環境を始めたばかりの人にとってはサーバーの目的になります。少しの創造性で、スクリプトをさらにカスタマイズし、スクリプトを他の用途に適合させることができます。
参照
- データベースメールの設定
- AmazonSESの使用を開始する
- リンクサーバー
- バックアップ履歴とヘッダー情報