SQL Server のバックアップをシステムデータベースを含めて、バックアップの取得元と異なるサーバーにリストアする際には、次の情報を参考にする機会が多いのではないでしょうか。
- システム データベースのバックアップと復元 (SQL Server)
- 孤立したユーザーのトラブルシューティング (SQL Server)
- SQL Server のスタンドアロン インスタンスをホストするコンピューターの名前を変更する
異なるサーバーにバックアップをリストアする際にはこれらの情報を参考にしてデータベースのリストアとリストア後の設定変更を行っていきます。
バックアップの取得元でレプリケーションが有効となっていた場合も、この作業だけでリストアを完了することができるのかを調査してみました。
概念検証として実施したため、T-SQL ベースで設定を強制的に変更している箇所がいくつかありますが、実運用環境で実施する際にはレプリケーションの再設定で検討をしたほうが良いかと思います。
Contents
リストアに必要な作業
リストアに必要となる作業を列挙していきたいと思います。
今回の作業環境は次のようになっています。
- SQL Server のバージョン: SQL Server 2019
- バックアップ取得元のサーバー名: REPL-2019-01
- レプリケーションのパブリッシャー / ディストリビューターとして構成されている
- リストア先のサーバー名: REPL-2019-03
- コマンドラインの実行: PowerShell
- ユーザーデータベース: tpch
- このデータベースをパブリケーションデータベースとして使用している
- SQL Server のサービスアカウントはドメインユーザーを使用している
- サービスマスターキーの移行の考慮は不要な状態
シングルユーザーモードで起動
master データベースをリストアする際には、シングルユーザーモードで起動をしておく必要があります。また、リストアを実行するセッション以外がシングルユーザーモードで起動している SQL Server に接続をすると、リストアができなくなるため、接続可能なプログラムを sqlcmd に限定しておきます。
これを実現するためには PowerShell で次のコマンドを実行します。
# SQL Server のサービス停止 Stop-Service MSSQLSERVER -Force # シングルユーザーモードで起動 Set-Location "C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\Binn" .\sqlservr.exe --% -sMSSQLSERVER -mSQLCMD
これにより接続可能なプログラムが制限された状態のシングルユーザーモードでリストアすることができます。
master データベースのリストア
シングルユーザーモードで起動が完了したら新しい PowerShell のウィンドウを開き、次のコマンドを実行します。
# sqlcmd で接続し、master のバックアップをリストア sqlcmd.exe --% -Q "RESTORE DATABASE master FROM DISK='<master のバックアップファイル>' WITH REPLACE"
これで master のリストアが完了します。
SQL Server のサービスの起動
シングルユーザーモードで起動していたインスタンスに対して master データベースのリストアを実施すると、SQL Server のインスタンスが停止した状態となりますので、次のコマンドでサービスを起動します。
# SQL Server のサービス開始 Start-Service MSSQLSERVER Start-Service SQLSERVERAGENT
システムデータベースのリストア
SQL Server のサービスが起動したら、SSMS で接続し、次のクエリでシステムデータベースをリストアします。
-- システムデータベースのリストア USE [master] GO ALTER DATABASE [msdb] SET SINGLE_USER WITH ROLLBACK IMMEDIATE RESTORE DATABASE [msdb] FROM DISK = N'<msdb のバックアップファイル>' WITH FILE = 1, NOUNLOAD, REPLACE, STATS = 5 ALTER DATABASE [msdb] SET MULTI_USER GO RESTORE DATABASE [model] FROM DISK = N'<model のバックアップファイル>' WITH FILE = 1, NOUNLOAD, REPLACE, STATS = 5 GO RESTORE DATABASE [distribution] FROM DISK = N'<distribution のバックアップファイル>' WITH FILE = 1, NOUNLOAD, REPLACE, STATS = 5, KEEP_REPLICATION GO
ポイントとしては「distribution」のリストアをする際に、「KEEP_REPLICATION」を指定していることでしょうか。
master をリストアすると、distribution のエントリもリストアされます。
後続のレプリケーション設定の初期化を実施するためには、distribution データベースもリストアしておく必要があるのですが、KEEP_REPLICATION を指定しないで distribution データベースをリストアするとシステムデータベースではなく、ユーザーデータベースとしてリストアされてしまうため、オプションを指定してリストアを実施しています。
ディストリビューターの強制解除
この後の作業としてはサーバー名の変更を実行したいのですが、現在の状態でサーバー名の変更を実行しようとすると次のようなエラーが発生します。(リモートログインはレプリケーションで使用している「distributor_admin」が該当しています)
メッセージ 15190、レベル 16、状態 1、プロシージャ sp_dropserver、行 56 [バッチ開始行 0]
サーバー ‘REPL-2019-01’ のリモート ログインまたはリンク ログインがまだ存在しています。メッセージ 20582、レベル 16、状態 1、プロシージャ sp_MSrepl_check_server、行 31 [バッチ開始行 0]
サーバー ‘REPL-2019-01’ を削除できません。レプリケーションでパブリッシャーとして使用されています。
また、レプリケーションを設定しているデータベースを削除しようとすると次のエラーが発生します。
メッセージ 3724、レベル 16、状態 3、行 10
データベース ‘tpch’ を 削除 できません。レプリケーションに使用されています。
これらのエラーを解消するため、次のクエリを実行してディストリビューターとしての登録を強制的に解除します。
-- ディストリビューターの強制解除 EXEC sp_dropdistributor @no_checks = 1,@ignore_distributor = 1 GO -- 強制解除後にレプリケーションしていたユーザーデータベースが削除できる DROP DATABASE [tpch] GO -- ユーザーデータベース削除後に再度強制解除を再実行し、ディストリビューターの設定を初期化 EXEC sp_dropdistributor @no_checks = 1,@ignore_distributor = 1 GO
最初の強制解除を実行すると、ディストリビューターの強制解除は完了しないのですが、ユーザーデータベースが削除できる状態となりますので、最初の強制解除後にユーザーデータベースのエントリの削除を実行しています。
ユーザーデータベースのエントリが削除された後に再度強制解除を実行することでディストリビューターの設定を削除することができます。
サーバー名の変更
ディストリビューターの設定の解除が実行出来たら、次のクエリを実行してサーバー名の変更を行います。
-- サーバー名の変更 SELECT @@SERVERNAME EXEC sp_dropserver'REPL-2019-01' GO EXEC sp_addserver 'REPL-2019-03', local; GO
クエリの実行が完了したら SQL Server のサービスを再起動します。
# SQL Server のサービス再起動 Restart-Service MSSQLSERVER -Force
これにより、SQL Server で新しいサーバー名の認識が行われます。
ディストリビューションの構成を実行
ここまでの作業が完了したら SSMS でディストリビューションの構成を実行し、ディストリビューターとして有効な状態に再設定します。
何回か検証を行うのであれば、ディストリビューションの構成をスクリプト化しておき GUI 操作を省略することも可能です。
ユーザーデータベースとdistribution データベースのリストア
レプリケーションの設定はユーザーデータベースと distribution データベースの両データベースに保持されています。
「KEEP_REPLICATION」オプションを使用してデータベースのリストアを行い、レプリケーションの設定を含めてリストアを行います。
-- ユーザーデータベースとディストリビューションのリストア USE [master] GO RESTORE DATABASE [tpch] FROM DISK = N'<ユーザーデータベースのバックアップファイル>' WITH FILE = 1, NOUNLOAD, REPLACE, STATS = 5, KEEP_REPLICATION GO ALTER DATABASE [distribution] SET SINGLE_USER WITH ROLLBACK IMMEDIATE RESTORE DATABASE [distribution] FROM DISK = N'<distribution のバックアップファイル>' WITH FILE = 1, NOUNLOAD, REPLACE, STATS = 5, KEEP_REPLICATION ALTER DATABASE [distribution] SET MULTI_USER GO
これでパブリッシャー / サブスクライバー / ディストリビューターの設定がリストアされた状態となります。
ユーザーデータベースの所有者の設定
レプリケーションはデータベースの所有者が設定されていないと動作しない機能となります。
そのため、所有者の SID で適切なログインの解決ができていないデータベースがあった場合、所有者として「sa」を設定するように次のクエリを実行して補正を行います。
-- DB 所有者の変更 USE [master] GO DECLARE @sql nvarchar(max) DECLARE @t TABLE(database_name sysname, owner varchar(100)) INSERT INTO @t EXEC sp_MSforeachdb 'USE [?];select ''?'' AS database_name, suser_sname((select sid from sys.database_principals where name = N''dbo''));' DECLARE alter_cursor CURSOR FOR SELECT 'ALTER AUTHORIZATION ON DATABASE::' + database_name + ' TO sa' AS alter_stmt FROM @t WHERE owner IS NULL OPEN alter_cursor FETCH NEXT FROM alter_cursor INTO @sql WHILE @@FETCH_STATUS = 0 BEGIN EXECUTE (@sql) FETCH NEXT FROM alter_cursor INTO @sql END CLOSE alter_cursor DEALLOCATE alter_cursor
distribution のサーバー情報の変更
distribution データベース内のレプリケーション設定に関するテーブルには、サーバー名が含まれている情報があります。
今回は「REPL-2019-01 → REPL-2019-03」にサーバーが変更されていますので、関連する情報のサーバー名も変更します。
-- DB 内のサーバー情報の変更 USE [distribution] GO UPDATE MSsubscriber_info SET publisher= 'REPL-2019-03' WHERE publisher = 'REPL-2019-01' UPDATE MSpublication_access SET sid = (SELECT sid FROM sys.server_principals WHERE name = 'distributor_admin') WHERE login = 'distributor_admin' UPDATE MSreplication_monitordata SET publisher = 'REPL-2019-03' WHERE publisher = 'REPL-2019-01' UPDATE MSsubscriber_schedule SET publisher = 'REPL-2019-03' WHERE publisher = 'REPL-2019-01' UPDATE MSreplservers SET srvname = 'REPL-2019-03' WHERE srvname = 'REPL-2019-01' GO
レプリケーションのジョブのサーバー情報の変更
レプリケーションのプロセスの起動は SQL Server エージェントのジョブで制御が行われています。
ジョブの情報内には、サーバ名の情報も含まれていますので、distribution と同様に関連する情報を書き換えます。データベースと同様に、ジョブも所有者の設定がありますので、解決できない SID が設定されているジョブについては sa を所有者として設定します。
-- レプリケーションのジョブのサーバー情報の変更 USE [msdb] GO UPDATE sysjobsteps SET server = 'REPL-2019-03' WHERE server = 'REPL-2019-01' GO -- レプリケーション系のジョブのステップ内のサーバー名の変更 UPDATE dbo.sysjobsteps SET command = REPLACE(command, 'REPL-2019-01', 'REPL-2019-03') WHERE command LIKE '%REPL-2019-01%' GO -- ジョブの所有者の変更 USE [msdb] GO UPDATE dbo.sysjobs SET owner_sid = 0x01 WHERE SUSER_NAME(owner_sid) IS NULL GO
SQL Server エージェントの再起動
最後に、レプリケーションに関してのジョブを再起動するため、SQL Server エージェントのサービスを再起動します。
# エージェントの再起動 Restart-Service SQLSERVERAGENT -Force
以上で作業は完了となります。
「DISTRIB.exe」「logread.exe」が起動しており、レプリケーションモニターでエラーが発生していなければ設定が期待通り移行できているはずです。
まとめ
本投稿では、レプリケーションが有効な環境のデータベースバックアップを異なるサーバーにリストアできるかを検証してみました。
技術的には既存のレプリケーションの設定を活かしながらリストアを進めていくことができましたが、本投稿の手順は、UI として提供がされていない「distribution」「msdb」内のサーバー情報を強制的に書き換えています。
この手順は運用環境での実行が推奨されるものではなく、何か問題が発生した場合の保証ができるものではありません。
レプリケーションの設定はスクリプト化することができ、レプリケーションの再設定にはスクリプトを再実行することで設定を復元することが望ましいと思います。
そのため、実運用環境ではレプリケーション設定のスクリプト化を行い、コード管理の中でレプリケーションの設定のバックアップをしておくことを検討するのが良いのではないでしょうか。