SQL Server のレプリケーションについて調べる機会があったので、その時に調べた概要と手順のメモです。

環境

初めに注意点

MySQL、PostgreSQL、Oracle 等他のデータベースでは、レプリケーションは Read 処理を複数のサーバーに分散するために使用され、もし Primary サーバーが故障した場合は、Replica サーバーの内の 1台が Primary の役割を引き継ぎ、システムを継続できます。

ただ SQL Server のレプリケーションは基本的に Read 処理を分散するためだけに用意されている機能で、Primary が故障した時に Replica が Primary の役割を引き継ぐ、ということができないので注意してください。

(Primary と Replica 両方で書き込みができるレプリケーション トポロジーもありますが、基本的には異なる拠点間でのデータ同期が主な目的で、Read 処理を分散するようなケースでは使用されないものと理解しています)

サーバー故障時に別のサーバーが役割を引き継いでシステムを継続する性能を “高可用性” と呼び、これは Availability Group という別の機能で実現されています。

Availability Group でも Read 処理の分散を実現できるため、後日このブログでも手順を紹介します。書きました – SQL Server Read Scale Availability Group

用語

SQL Server のレプリケーションでは独特な用語を使うため、ここで概要を説明します。

用語説明
Publisherレプリケーション元のサーバーを表します。
Publicationレプリケーションの論理的な単位を表します。
Publication はデータベース毎に作成されます。
Publication DatabasePublication が定義されている、レプリケーション元のデータベースを表します。
ArticlePublication に含まれる、レプリケーションされる個々のオブジェクト (テーブルや Stored Procedure など) を表します。
1つの Publication に、複数の Article が含まれます。
Subscriberレプリケーション先のサーバーを表します。
Subscriptionレプリケーション データを受け取る論理的な単位を表します。
Subscription は Publication が公開したデータを受け取り、変更をレプリケーション先のデータベースへ適用します。
Subscription Databaseレプリケーション先のデータベースを表します。
DistributorPublisher から Subscriber へデータを届ける役割を持つサーバーを表します。
Distribution Databaseレプリケーションに関するメタデータなどが保存されるデータベースを表します。
レプリケーションされるデータも一時的に Distribution Database に保存されます。

なお、Distributor は Publisher と同じサーバーに構成することもできます。

Publisher が停止するとレプリケーションも止まるため、複数の Publisher で同じ Distributor を共有するような構成でなければ、基本的に Publisher と同じサーバーに Distributor を構成するので問題は無いと思います。

レプリケーションの種類

SQL Server では多くの種類のレプリケーションを利用でき、大きく分けて3つに分類できます。

  1. スナップショット レプリケーション
  2. マージ レプリケーション
  3. トランザクション レプリケーション

スナップショット レプリケーション

スナップショット レプリケーションは、定期的にデータベースのスナップショットを同期します。

スナップショットはある時点のデータを表しますので、レプリケーション先のデータベースでは常に最新のデータが参照できるわけではありません。

マージ レプリケーション

Publisher と Subscriber の間で定期的に変更データを交換する形でレプリケーションが行われます。

Publisher と Subscriber 両方でデータ変更を行うことが可能で、トリガーによって定期的に両サーバー間で変更データが交換されます。

トリガーによる同期のため、Publisher と Subscriber 双方で常に最新のデータが利用可能になるわけではありません。

トランザクション レプリケーション

MySQL 等の他のデータベースで一般的に行われるレプリケーションの方法で、Subscriber は Publisher からトランザクションログを受け取り、そのトランザクションを復元することでデータを複製します。

トランザクションレプリケーションでは、Subscriber は読み取り専用です。

ただトランザクション レプリケーションをベースにした他のレプリケーション方式では、Subscriber での更新もサポートされています (双方向レプリケーション、ピアツーピアレプリケーション、更新可能なサブスクリプション など)。

この記事で扱うレプリケーション

今回は基本的な トランザクション レプリケーション についてのみ扱います。この記事で “レプリケーション” と言った場合、トランザクション レプリケーションを指しますのでご注意ください。

レプリケーション エージェント

トランザクション レプリケーションでは、複数の “エージェント” と呼ばれるプロセスが協調してデータの複製を行います。

関連する主なエージェントは以下の3つです。

エージェント名説明
Snapshot Agentレプリケーション開始時に、データベースのスナップショットを取得するプロセス。
Publication に新しいテーブルを追加した時などにも、テーブルのスナップショットが取得されます。
Log Reader AgentPublisher でコミットされたトランザクションログを取得し、Distribution Database へ保存するプロセス。
Distribution AgentDistribution Database から変更データを取得し、Subscription Database へ適用するプロセス。

各エージェントは SQL Server Agent という、SQL Server でジョブをスケジュールするプロセスから起動されます。そのためレプリケーションを行う時は、SQL Server Agent を起動しておく必要があります。

Subscription の種類

Subscriber がデータを受け取る方法には、”Push Subscription” と “Pull Subscription” の 2種類あります

Push Subscription では Distribution Agent は Distributor で動作し、Distributor から Subscriber へアクセスし変更データを適用します。

Pull Subscription では Distribution Agent が Subscriber で動作し、Subscriber から Distributor へアクセスして変更データを取得し、Subscriber 自身のデータベースへ変更データを適用します。

Distributor と Subscriber のアクセスの方向が異なるため、ユーザー権限の設定や Firewall の設定などで手順が異なります。

今回は “Push Subscription” でレプリケーションを行う手順を説明します。

レプリケーション設定手順

それではトランザクション レプリケーションを構成します。

レプリケーションの構成は、まず Distributor の設定を行い、次に Publication と Article を作成し、最後に Subscription を作成するという手順で行います。

SQL Server Management Studio を使って GUI で設定する方法は 公式サイト にチュートリアルがありますので、今回は全てコマンドで設定する方法を紹介します。

レプリケーション構成の概要

今回、以下の構成でレプリケーションを設定します。

各サーバーの情報は以下の通りです。

また各エージェント プロセスを実行する Windows ユーザーは以下とします。

1). 事前準備

1-1). リモートアクセス設定

今回は Push Subscription なので、Distributor から Subscriber へアクセスできる必要があります。

レプリケーションでは コンピューター名でアクセス するため、Distributor では Subscriber のコンピューター名で名前解決ができる必要があります。

今回は hosts にコンピューター名を記述して名前解決することにします。

Publisher サーバー

> notepad C:\Windows\System32\drivers\etc\hosts
---
# 以下を追記
172.16.11.15 MSSQL2
---

続いて Subscriber で SQL Server の TCP 接続を有効にし、Firewall を許可します。

Subscriber サーバー

# SQL Server で TCP を許可
> sqlps
PS> $wmi = New-Object 'Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer' localhost
PS> $tcp = $wmi.ServerInstances['MSSQLSERVER'].ServerProtocols['Tcp']
PS> $tcp.IsEnabled = $true
PS> $tcp.Alter()
PS> Restart-Service -Name MSSQLSERVER -Force
PS> exit 

# Firewall でポート 1433 を許可
> netsh advfirewall firewall add rule name="allow-sql-server" dir=in action=allow enable=yes profile=any protocol=tcp localport=1433 remoteip=172.16.11.14 remoteport=any

Publisher で接続確認を行います。Subscriber のコンピューター名を指定して接続できれば OK です。

Publisher サーバー

> sqlcmd -S MSSQL2 -U sa -P password
1> SELECT name FROM sys.databases;
2> GO
----------
master
tempdb
model
msdb
(4 行処理されました)

1-2). エージェント実行ユーザーの作成

Publisher で、各エージェントを実行する Windows ユーザーを作成します。

Publisher サーバー

# ユーザーを作成し、パスワードの有効期限を無効に設定
> net user /add repl_snapshot password /passwordchg:no
> wmic useraccount where Name='repl_snapshot' set PasswordExpires=False

> net user /add repl_logreader password /passwordchg:no
> wmic useraccount where Name='repl_logreader' set PasswordExpires=False

> net user /add repl_distribution password /passwordchg:no
> wmic useraccount where Name='repl_distribution' set PasswordExpires=False

今回は Push Subscription なので、Distribution Agent は Subscriber にアクセスしてデータ変更を適用します。

そのため Subscriber 側でも Distribution Agent の実行ユーザーと同じ名前、同じパスワードの Windows ユーザーを作成しておきます。

Subscriber サーバー

# ユーザーを作成し、パスワードの有効期限を無効に設定
> net user /add repl_distribution password /passwordchg:no
> wmic useraccount where Name='repl_distribution' set PasswordExpires=False

1-3). Snapshot 用フォルダの作成

レプリケーションが開始する時、まず初めに各テーブルのスナップショットが作成されて Subscriber に適用されます。スナップショットはファイルとして出力され、bcp というツールで一括登録されます。

スナップショットの出力を保存するためのフォルダは予め作成しておく必要があります。

今回、スナップショット用フォルダは SQL Server のデータディレクトリに ReplData という名前で作成します。併せて Snapshot Agent 実行ユーザーと Distribution Agent 実行ユーザーにそれぞれ権限を付与します。

Publisher サーバー

# Snapshot 用フォルダを作成
> mkdir "C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\ReplData"

# 各エージェント実行ユーザーに権限付与
#   - Snapshot Agent ユーザー: フルコントロール
#   - Distribution Agent ユーザー: 読み取り 
> icacls "C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\ReplData" ^
  /grant repl_snapshot:(OI)(CI)F ^
  /grant repl_distribution:(OI)(CI)R

ちなみに、スナップショットファイルは Publisher で動作する Snapshot Agent により書き出され、Distribution Agent によって Subscriber に適用されますので、各エージェントが異なるサーバーで動作する場合 (Pull Subscription 等) は、スナップショット用フォルダを共有フォルダとして公開する必要があるなど、追加の手順が必要となります。

今回は 2つのエージェントが同じサーバーで動作するため特別な設定は不要です。

1-4). SQL Server Agent を起動

各エージェントプロセスは SQL Server Agent によって起動されますので、先に SQL Server Agent を起動しておきます。

SQL Server Agent は Windows サービスのため、自動起動の設定も有効にしておきます。

Publisher サーバー

# 自動起動を有効化
> sc config "SQLSERVERAGENT" start=auto

# SQL Server Agent を起動
> net start "SQLSERVERAGENT"

1-5). レプリケーション対象のデータベース作成

今回は testdb というデータベースをレプリケーションします。

Publisher サーバー

/* データベースを作成 */
CREATE DATABASE testdb COLLATE Japanese_CI_AS;
GO

USE testdb;
GO

/* レプリケーションするテーブルを作成 */
CREATE TABLE table1 (id INT IDENTITY PRIMARY KEY, name NVARCHAR(255));
GO

BEGIN TRAN
INSERT INTO table1 (name) VALUES (N'name 1-1'), (N'name 1-2');
COMMIT

CREATE TABLE table2 (id INT IDENTITY PRIMARY KEY, name NVARCHAR(255));
GO

BEGIN TRAN
INSERT INTO table2 (name) VALUES (N'name 2-1'), (N'name 2-2');
COMMIT

Subscriber でも、レプリケーション先のデータベースを予め作成しておく必要があります。

(GUI で設定を行う場合は、Subscription を作成するタイミングで Subscriber 側のデータベースが自動で作成されるのですが、コマンドで設定する場合は予め自分で作成しておく必要があります)

今回は testdb_replica というデータベース名にします。

Subscriber サーバー

/* レプリケーション先のデータベースを作成 */
CREATE DATABASE testdb_replica COLLATE Japanese_CI_AS;
GO

1-6). SQL Server LOGIN の作成と権限設定

各エージェント実行ユーザーは SQL Server にアクセスするため、SQL Server LOGIN として登録しておく必要があります。

またレプリケーション元のデータベースへもアクセスするため、db_owner ロールを付与しておきます。

Publisher サーバー

USE master;
GO

/* SQL Server LOGIN を作成 */
CREATE LOGIN [MSSQL1\repl_snapshot] FROM WINDOWS;
CREATE LOGIN [MSSQL1\repl_logreader] FROM WINDOWS;
CREATE LOGIN [MSSQL1\repl_distribution] FROM WINDOWS;
GO

USE testdb;
GO

/* USER を作成し db_owner ロールを付与 */
CREATE USER [MSSQL1\repl_snapshot] FOR LOGIN [MSSQL1\repl_snapshot];
CREATE USER [MSSQL1\repl_logreader] FOR LOGIN [MSSQL1\repl_logreader];
CREATE USER [MSSQL1\repl_distribution] FOR LOGIN [MSSQL1\repl_distribution];

ALTER ROLE db_owner ADD MEMBER [MSSQL1\repl_snapshot];
ALTER ROLE db_owner ADD MEMBER [MSSQL1\repl_logreader];
ALTER ROLE db_owner ADD MEMBER [MSSQL1\repl_distribution];
GO

Subscriber 側でも、Distribution Agent の実行ユーザーがアクセスできるよう SQL Server LOGIN を作成し、宛先となるデータベースへの権限を付与します。

Subscriber サーバー

USE master;
GO

/* SQL Server LOGIN を作成 */
CREATE LOGIN [MSSQL2\repl_distribution] FROM WINDOWS;
GO

USE testdb_replica;
GO

/* USER を作成し db_owner ロールを付与 */
CREATE USER [MSSQL2\repl_distribution] FOR LOGIN [MSSQL2\repl_distribution];
ALTER ROLE db_owner ADD MEMBER [MSSQL2\repl_distribution];
GO

2). Distributor の設定

それでは Distributor の設定を行います。

Distributor の設定は、

  1. Distributor を構成
  2. Distribution Database を作成
  3. 作成した Distribution Database を使うよう Publisher を設定
  4. Distribution Database への権限を各エージェント実行ユーザーに付与

という手順で行います。

Publisher サーバー

USE master;
GO

/* Distributor を構成 */
EXEC sp_adddistributor
  @distributor = N'MSSQL1',  /* Distributor のコンピューター名 */
  @password = N'';  /* 空文字または NULL でパスワードを自動生成 */
GO

/* Distribution Database を作成 */
EXEC sp_adddistributiondb
  @database = N'distribution',  /* Distribution Database 名 */
  @data_file = N'distribution.MDF',  /* データファイル名 */
  @data_file_size = 15, /* データファイルの初期サイズ (MB) */
  @log_file = N'distribution.LDF', /* ログファイル名 */
  @log_file_size = 10,  /* ログファイルの初期サイズ (MB) */
  @max_distretention=72, /* トランザクションが削除されるまでの最大保有期間(hour) */
  @history_retention = 48, /* 履歴を保持する最大保有期間 (hour) */
  @security_mode = 1;  /* 認証方法 - "1" は Windows 認証 */
GO

RECONFIGURE;
GO

/* Publisher を構成 */
EXEC sp_adddistpublisher
  @publisher = N'MSSQL1',  /* Publisher のコンピューター名 */
  @distribution_db = N'distribution',
  @working_directory = N'C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\ReplData';  /* Snapshot 用フォルダ */
GO

/* Distribution Database への権限設定 */
USE distribution;
GO

CREATE USER [MSSQL1\repl_snapshot] FOR LOGIN [MSSQL1\repl_snapshot];
CREATE USER [MSSQL1\repl_logreader] FOR LOGIN [MSSQL1\repl_logreader];
CREATE USER [MSSQL1\repl_distribution] FOR LOGIN [MSSQL1\repl_distribution];

ALTER ROLE db_owner ADD MEMBER [MSSQL1\repl_snapshot];
ALTER ROLE db_owner ADD MEMBER [MSSQL1\repl_logreader];
ALTER ROLE db_owner ADD MEMBER [MSSQL1\repl_distribution];
GO

sp_adddistributiondb

Distribution Database を作成する時に使用した sp_adddistributiondb について補足です。

この Stored Procedure では、作成する Distribution Database (SSMS 上ではシステムデータベースとして表示されます) のデータファイルやトランザクションログファイルの名前などを設定でき、加えて同期するトランザクションログをどの程度の期間保持するかなども設定できます。

@max_distretention パラメータでは、Distribution Database にトランザクションログを保持する期間を指定します。デフォルトは 72時間で、トランザクションログが 72時間以内に Subscriber に適用されなかった場合、その変更データは消失します。

@history_retention は、Publisher から公開され、Subscriber に適用されたトランザクションログの履歴をどの程度保持するかを指定するパラメータです。デフォルトは 48時間で、レプリケーションされたトランザクションの履歴は 48時間後に自動的に消去されます。

3). Publication の作成

次に Publication を作成します。この手順では以下を行います。

  1. Publication Database を設定
  2. Log Reader Agent を追加
  3. Publication を作成
  4. Snapshot Agent を追加
  5. Article を追加
  6. Snapshot Agent を開始
  7. Distribution Agent 実行ユーザーを Publication のアクセスリストに追加

Publisher サーバー

USE testdb;
GO

/* Publication Database を設定 */
EXEC sp_replicationdboption 
  @dbname = N'testdb',
  @optname = N'publish',
  @value = N'true';
GO

/* Log Reader Agent を追加 */
EXEC sp_addlogreader_agent
  @publisher_security_mode = 1,  /* 認証方法 - "1" は Windows 認証 */
  @job_login = N'MSSQL1\repl_logreader',
  @job_password = N'password';
GO

/* Publication の作成 */
EXEC sp_addpublication
  @publication = N'testdb_tran_publication', /* Publication 名 */
  @description = N'Transactional publication of testdb from Publisher MSSQL1',
  @allow_push = N'true',  /* Push Subscription 許可設定 */
  @allow_pull = N'true',  /* Pull Subscription 許可設定 */
  @repl_freq = N'continuous',
  @status = N'active',
  @independent_agent = N'true',
  @immediate_sync = N'true';
GO

/* Snapshot ジョブを追加 */
EXEC sp_addpublication_snapshot 
  @publication = N'testdb_tran_publication',  /* Publication 名 */ 
  @publisher_security_mode = 1,  /* 認証方法 - "1" は Windows 認証 */
  @job_login = N'MSSQL1\repl_snapshot', 
  @job_password = N'password', 
  @frequency_type = 1,  /* Agent実行頻度。繰り返し実行しない場合は "1" */
  @frequency_interval = 0, 
  @frequency_relative_interval = 0,
  @frequency_subday = 0, 
  @frequency_subday_interval = 0;
GO

/* Article を追加 */
EXEC sp_addarticle
  @publication = N'testdb_tran_publication',
  @article = N'table1',  /* Article 名。Publication 内で一意とする必要がある */
  @source_owner = N'dbo',  /* 同期するテーブルの所有者 */
  @source_object = N'table1',  /* 同期するテーブル */
  @type = N'logbased',  /* 同期方法。テーブルの場合は "logbased" を指定 */
  @schema_option = 0x00000000080350DB, 
  @identityrangemanagementoption = N'manual',
  @destination_table = N'table1',  /* 同期先のテーブル名 */
  @destination_owner = N'dbo';  /* 同期先のテーブル所有者 */
GO

EXEC sp_addarticle
  @publication = N'testdb_tran_publication',
  @article = N'table2',
  @source_owner = N'dbo',
  @source_object = N'table2',
  @type = N'logbased',
  @schema_option = 0x00000000080350DB, 
  @identityrangemanagementoption = N'manual', 
  @destination_table = N'table2', 
  @destination_owner = N'dbo';
GO


/* Snapshot agent job を実行 */
EXEC sp_startpublication_snapshot
  @publication = N'testdb_tran_publication';
GO

/* Distribution Agent の実行ユーザーを Publication のアクセスリスト (PAL) に追加 */
EXEC sp_grant_publication_access
  @publication = N'testdb_tran_publication',
  @login = N'MSSQL1\repl_distribution';
GO

上記で実行した Stored Procedure について少し補足します。

sp_addpublication

Publication を作成した時に使った sp_addpublication のパラメータ @repl_freq では、レプリケーションの頻度を設定します。トランザクションレプリケーションでは “continuous” と設定します。

sp_addpublication_snapshot

Snapshot Agent を追加する時に使った sp_addpublication_snapshot では、Snapshot Agent を繰り返し実行せず、1度のみ実行するよう設定しています。

トランザクションレプリケーションでは、トランザクションログが随時適用されてデータが複製されるため、スナップショットは初めに適用される時以外は必要ありません。

sp_addpublication@immediate_sync=true と指定したので、Publication に新しいテーブルが新しいテーブルが追加される度に Snapshot が取得、適用されます。

sp_addarticle

Article を追加するために使う sp_addarticle は、同期するテーブルや Stored Procedure 毎に実行します。今回は 2つのテーブルを同期するため、2回実行しました。

@schema_option パラメータでは、主キーや外部キーなども同期するかなど、どのスキーマを同期するかを指定することができます。数が多いので詳細は 公式ドキュメント を参照してください。テーブルを同期する場合は 0x00000000080350DB と指定するのが良いと思います。

@identityrangemanagementoption は IDENTITY 列のレプリケーションに関する設定です。Subscriber が読み取り専用の場合、manual に設定します。

エージェントの状態

なお、この時点で Snapshot Agent が開始され、スナップショットファイルが指定したフォルダに保存されています。

また Log Reader Agent が開始され、Publisher のトランザクションログが取得されて Distribution Database に保存されます。

4). Subscription の作成

最後に Subscription を作成します。

Subscription の作成も Publisher サーバーで行いますので注意してください。

Publisher サーバー

USE testdb;
GO

/* Subscription を追加 */
EXEC sp_addsubscription 
  @publication = N'testdb_tran_publication', /* Publication 名 */
  @subscriber = N'MSSQL2',  /* Subscriber のコンピューター名 */
  @destination_db = N'testdb_replica', /* Subscription Database */
  @subscription_type = N'push', /* Subscription の種類 */
  @article = N'all',  /* 同期する Article */
  @sync_type = N'automatic',
  @update_mode = N'read only';
GO

/* Distribution Agent (Push Subscription) を追加 */
EXEC sp_addpushsubscription_agent 
  @publication = N'testdb_tran_publication', /* Publication 名 */ 
  @subscriber = N'MSSQL2',   /* Subscriber のコンピューター名 */
  @subscriber_db = N'testdb_replica', /* Subscription Database */ 
  @subscriber_security_mode = 1, /* 認証方法 - "1" は Windows 認証 */
  @job_login = N'MSSQL1\repl_distribution', 
  @job_password = N'password';
GO

これで Distribution Agent が開始し、スナップショットファイルを Subscriber に適用した後、Distribution Database から変更データを取得して Subscription Database へ適用します。

以上で SQL Server のトランザクションレプリケーションの構成は完了です。