SQL Serverのシステムデータベースをリストアする

:::note info
本稿は、[ZOZO Advent Calendar 2024 シリーズ 6](https://qiita.com/advent-calendar/2024/zozo) の20日目の記事です。
:::

# はじめに
ZOZOTOWNでは、サービスのメインデータベースとしてSQL Serverを採用しています。通常、SQL Serverのシステムデータベースをリストアする機会は少ないものの、その手順はユーザーデータベースとは異なるため、今回の記事でその方法を記録しておきたいと思います。

# SQL Serverのシステムデータベースとは
SQL Serverのシステムデータベースは、SQL Serverの運用や管理に不可欠なデータベースで、SQL Serverインスタンスの動作を支える役割を果たします。これらのデータベースはユーザーデータベースとは異なり、主にSQL Server自体の内部処理や構成情報を保持しています。

https://learn.microsoft.com/ja-jp/sql/relational-databases/databases/system-databases?view=sql-server-ver16

普段はあまり操作することはありませんが、システムデータベースが存在しないとSQL Serverは動作しないため、非常に重要な役割を担っています。

# システムデータベースのリストアを検証する
システムデータベースにはさまざまな種類がありますが、今回は運用でリストアが必要になる可能性が高い3つのデータベースに絞って検証を行います。また、それぞれのデータベースが保持する構成情報が正しく復元できるかについても確認します。

### 対象データベース
- master
- model
- msdb

# master
SQL Serverインスタンス全体の設定や情報を管理する最も重要なデータベースです。
ログイン情報やサーバ設定といったSQL Serverの構成情報に加え、各データベースの作成日時や格納場所などのメタデータを保持しています。今回はメモリ割り当てとログインを作成して設定値やオブジェクトが復元されるか確認します。

### 事前準備
「kazunari_saito」というログインを作成し、メモリ割り当てを8192MBに設定する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634756/02891481-be76-8e43-57fe-5537cf627bed.png)

masterデータベースのバックアップを取得します。
```sql
USE [master]
GO
 
BACKUP DATABASE master
TO DISK='<backup destination path>'
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634756/07a857f1-0621-683e-2282-7b3bcbbc8137.png)

メモリ割り当て値を10240MBに変更し、作成した「kazunari_saito」ログインを削除します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634756/f88b7ad3-2ed0-eb0e-76c9-69e2ab7ab846.png)

### masterデータベースをリストアする
SQL Server構成マネージャーを起動してSQL Serverに関連するサービスを全て停止します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634756/bf525cb9-d89f-53bf-fa9b-9288ae3ffa49.png)

次に、コマンドプロンプトを管理者権限で起動し、SQL Serverのインストールディレクトリ内の「Binn」フォルダにある「sqlservr.exe」からSQL Serverをシングルユーザーモードで起動します。
```sql
> cd C:\Program Files\Microsoft SQL Server\MSSQL16.MSSQLSERVER\MSSQL\Binn
> sqlservr.exe -m
```
以下の画像のようになれば起動が成功しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634756/2e0ea261-aaf4-d2c2-dd01-6daec6c47986.png)

:::note warn
コマンドプロンプトを閉じてはいけません。
:::

masterデータベースを**リストアする方法は主に2種類**あります。
1つはローカル環境でのSQLCMDを使用する方法、もう1つはSQL Server Management Studio（SSMS）を使用する方法です。

**【SQLCMDを使用する方法】**
SQL Serverをシングルユーザーモードで起動した際のコマンドプロンプトとは別にもう一つコマンドプロンプトを管理者権限で起動し、SQLCMDで接続してリストアコマンドを実行します。
```sql
> sqlcmd -E -S <hostname>
1>RESTORE DATABASE master FROM DISK = '<backup file path>' WITH REPLACE
2>GO
```
masterデータベースが復元されるとSQL Serverは自動的にシャットダウンされます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634756/3edfeabb-0508-c4da-6535-361ff6196f98.png)


**SQL Server Management Studio（SSMS）を使用する方法**
SQL Serverをシングルユーザーモードで起動した後、SSMSを使用してクエリウィンドウを開きます。この際、シングルユーザーモードでは1セッションのみ接続可能なため、オブジェクトエクスプローラーなどの追加セッションを開かないようにしてください。
クエリウィンドウからリストアコマンドを実行します。
```sql
USE [master]
GO
 
RESTORE DATABASE master FROM DISK = '<backup file path>' WITH REPLACE
GO
```
masterデータベースが復元されるとSQL Serverは自動的にシャットダウンされます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634756/195121cd-521f-d494-fc80-864c9f769b9b.png)

### 設定値やオブジェクトの確認
SQL Server構成マネージャーを起動してSQL Serverのサービスを起動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634756/206a53ad-df27-a7ab-f7c9-efb10bdf7405.png)

SQL Serverにログインして確認してみると「kazunari_saito」というログインが復元され、メモリ割り当ての設定値もバックアップ取得時の8192MBに戻っていることが確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634756/3638461d-6ae8-5c37-88c8-f8ab01dd7542.png)

# model
ユーザーデータベースを作成するときのテンプレートとなるデータベースです。
今回は、modelデータベースにテンプレート用のテーブルを作成し、そのテーブルが復元されるかを検証します。また、新たにユーザーデータベースを作成した際に、そのテンプレートが正常に機能するかも併せて確認します。

### 事前準備
modelデータベースにテーブルを作成しデータを挿入しておきます。
```sql
USE [model]
GO
 
SET ANSI_NULLS ON
GO
 
SET QUOTED_IDENTIFIER ON
GO
 
CREATE TABLE [dbo].[model_restore_test](
    [id] [bigint] NOT NULL
) ON [PRIMARY]
GO
 
INSERT INTO [dbo].[model_restore_test] VALUES (1)
 
SELECT * FROM [dbo].[model_restore_test]
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634756/75def5da-2818-3693-e590-b21f1eb4521b.png)


modelデータベースのバックアップを取得します。
```sql
USE [master]
GO
 
BACKUP DATABASE model
TO DISK='<backup destination path>'
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634756/cd487fec-eb8a-0fa9-e181-9e2a61eda39f.png)


作成した「model_restore_test」テーブルにデータを追加で挿入します。
```sql
USE [model]
GO
 
INSERT INTO [dbo].[model_restore_test] VALUES (2)
 
SELECT * FROM [dbo].[model_restore_test]
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634756/361bbbbe-89ee-eaf2-b7a1-674faffb1c8f.png)

### modelデータベースをリストアする
SSMSを使用してSQL Serverにログインし、データベースをシングルユーザーモードに変更してリストア操作を実行します。
```sql
USE master
GO
 
ALTER DATABASE model SET SINGLE_USER WITH ROLLBACK IMMEDIATE
GO
 
RESTORE DATABASE model
FROM DISK = '<backup file path>'
WITH REPLACE
 
ALTER DATABASE model SET MULTI_USER
GO
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634756/dc59c12a-666d-ece0-1dfa-932d55c08735.png)

### テーブルの復元とテンプレート機能の正常性確認
「model_restore_test」テーブルは正常に復元され、バックアップ取得時のデータ件数1件の状態で復元されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634756/f6444f87-446c-55fd-cc5f-3e7496716675.png)

新規に「testdb2」データベースを作成したところ、その配下に「model_restore_test」テーブルが作成され、1件のデータが挿入されている状態になっていました。このことから、テンプレートとしての機能が正常に動作していることが確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634756/f74e45d0-77eb-ce90-9957-5a1642cd4373.png)

# msdb
SQL Serverエージェントのジョブやスケジュール情報、バックアップ履歴などを管理しているデータベースです。今回は、レプリケーションの復元が可能かを検証します。レプリケーションの復元には、masdbに加え、distributionデータベースとユーザーデータベースの復元が必要です。レプリケーションの動作に必要なSQL Serverエージェントジョブはmsdbが管理しているため、レプリケーション復元の過程でジョブの復元が確認できます。

### 事前準備
テスト用データベース「testdb」を作成し、その中に「repl_test」テーブルを準備してデータを挿入します。

```sql
USE [testdb]
GO
 
SET ANSI_NULLS ON
GO
 
SET QUOTED_IDENTIFIER ON
GO
 
CREATE TABLE [dbo].[repl_table](
    [id] [bigint] NOT NULL PRIMARY KEY
) ON [PRIMARY]
GO
 
INSERT INTO [dbo].[repl_table] VALUES (1)
 
SELECT * FROM [dbo].[repl_table]
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634756/1398b881-748a-cbc7-4041-c6a81bccfddf.png)

レプリケーションを有効化し、別インスタンスのデータベースと同期を行いました。この操作により、distributionデータベースが作成され、レプリケーションに必要なSQL Serverエージェントジョブも生成されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634756/ebe7d279-92db-cf36-9b40-29cd892d7eb9.png)

msdb,distribution,testdbの各データベースのバックアップを取得します。
```sql
USE [master]
GO
 
BACKUP DATABASE msdb
TO DISK='<backup destination path>'
 
BACKUP DATABASE distribution
TO DISK='<backup destination path>'

BACKUP DATABASE testdb
TO DISK='<backup destination path>'
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634756/e392386d-bae7-7579-2fd5-6f2df32ad817.png)

パブリケーションを削除します。この時点で同期が停止します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634756/554a3a40-1f85-3a08-d8e9-1ae595d5f626.png)

ジョブも削除されていることが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634756/f175717a-31ae-bf3c-4256-ce56e65e2b3c.png)

### msdb,distribution,testdbの各データベースをリストアする
SSMSを使用してSQL Serverにログインし、各データベースをシングルユーザーモードに変更してリストア操作を実行します。
```sql
USE master
GO
 
ALTER DATABASE distribution SET SINGLE_USER WITH ROLLBACK IMMEDIATE
GO
 
RESTORE DATABASE distribution
FROM DISK = '<backup file path>'
WITH REPLACE
 
ALTER DATABASE distribution SET MULTI_USER
GO
 
ALTER DATABASE msdb SET SINGLE_USER WITH ROLLBACK IMMEDIATE
GO
 
RESTORE DATABASE msdb
FROM DISK = '<backup file path>'
WITH REPLACE
 
ALTER DATABASE msdb SET MULTI_USER
GO

ALTER DATABASE testdb SET SINGLE_USER WITH ROLLBACK IMMEDIATE
GO
 
RESTORE DATABASE testdb
FROM DISK = '<backup file path>'
WITH REPLACE
 
ALTER DATABASE testdb SET MULTI_USER
GO
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634756/ee4d2e9c-e1be-1172-cb4c-eca083d304d0.png)

### レプリケーションが復元されているか確認
削除したパブリケーションやサブスクリプションが復元されており、同様にSQL Serverエージェントジョブも正常に復元されていました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634756/1f7ac70f-4757-8b72-7e14-5104c597fc5d.png)

復元直後の状態ではレプリケーションがエラーとなっており、ログリーダーエージェントとディストリビューションエージェントの手動起動が必要でした。その後、「testdb」データベースの「repl_table」テーブルにデータを挿入したところ、サブスクライバー側への同期が正常に行われることを確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634756/0901f571-b81f-bc0a-a0a6-6cd893dad595.png)

# まとめ
SQL Serverのシステムデータベースのリストアについて検証を行いました。システムデータベースのリストアは、DBサーバをゼロから構築する際に、バックアップからインスタンスの設定やオブジェクトを復元できるため、大規模な災害復旧をはじめ、さまざまな場面で活用できると考えています。しかし、通常の運用では操作する機会が少ないため、いざ使用する際に手順が明確になっていない可能性があります。そのため、緊急時に備えて、手順や準備を事前に整えておくことが重要だと感じました。