Always On 可用性グループ用に分散トランザクションを構成する
適用対象 SQL Server
SQL Server 2017 (14.x) 以降のバージョンでは、可用性グループのデータベースを含むすべての分散トランザクションがサポートされます。 この記事では、分散トランザクションの可用性グループを構成する方法について説明します。
分散トランザクションを保証するには、分散トランザクション リソース マネージャーとしてデータベースを登録するように、可用性グループを構成する必要があります。
Note
SQL Server 2016 (13.x) Service Pack 2 以降のバージョンでは、可用性グループでの分散トランザクションが完全にサポートされます。 SQL Server 2016 (13.x) Service Pack 1 以前のバージョンでは、可用性グループ内のデータベースに関連する複数データベースにまたがる分散トランザクション (つまり、同じ SQL Server インスタンスのデータベースを使用するトランザクション) はサポートされません。 SQL Server 2017 (14.x) にはこのような制限はありません。
SQL Server 2016 (13.x) では、構成手順は SQL Server 2017 (14.x) の場合と同じです。
分散トランザクションでは、クライアント アプリケーションは Microsoft 分散トランザクション コーディネーター (MSDTC または DTC) と連携して、複数のデータ ソース間でトランザクションの整合性を保証します。 DTC は、サポートされている Windows Server ベースのオペレーティング システムで使用可能なサービスです。 分散トランザクションの場合は、DTC が "トランザクション コーディネーター" です。 通常は、SQL Server インスタンスが "リソース マネージャー" です。 データベースが可用性グループ内にある場合、各データベースがそれ自体のリソース マネージャーである必要があります。
可用性グループが分散トランザクション用に構成されていない場合であっても、SQL Server は可用性グループ内のデータベースに対する分散トランザクションを妨げません。 ただし、可用性グループが分散トランザクション対応に構成されていない場合は、一部の状況でフェールオーバーが失敗する可能性があります。 具体的には、新しいプライマリ レプリカの SQL Server インスタンスが、DTC からトランザクションの結果を取得できない場合があります。 SQL Server インスタンスがフェールオーバー後に DTC から未確定トランザクションの結果を取得できるようにするには、可用性グループを分散トランザクション対応に構成します。
データベースがフェールオーバー クラスターのメンバーでもある場合を除き、DTC は可用性グループの処理には関与しません。 可用性グループ内では、レプリカ間の整合性は可用性グループのロジックによって維持されます。プライマリはコミットを完了せず、セカンダリが永続的なストレージにログ レコードを保持したを確認するまで、呼び出し元に対するコミットを承認します。 その後でのみ、プライマリによってトランザクションの完了が宣言されます。 非同期モードでは、セカンダリからの肯定応答を待たないため、少量のデータが失われる可能性が明示的に存在します。
前提条件
分散トランザクションをサポートするように可用性グループを構成するには、次の前提条件が満たされている必要があります。
分散トランザクションに参加する SQL Server のすべてのインスタンスが、SQL Server 2016 (13.x) またはそれ以降のバージョンである必要があります。
可用性グループは、Windows Server 2012 R2 以降のバージョンで実行されている必要があります。 Windows Server 2012 R2 の場合は、KB3090973 の更新プログラムをインストールする必要があります。
分散トランザクション対応の可用性グループを作成する
分散トランザクションをサポートするように可用性グループを構成します。 各データベースがリソース マネージャーとして登録するのを許可するように、可用性グループを設定します。 この記事では、各データベースが DTC のリソース マネージャーになることができるように、可用性グループを構成する方法について説明します。
SQL Server 2016 (13.x) 以降のバージョンでは、分散トランザクション対応の可用性グループを作成できます。 分散トランザクション対応の可用性グループを作成するには、可用性グループの定義に DTC_SUPPORT = PER_DB
を追加します。 次のスクリプトは、分散トランザクション対応の可用性グループを作成します。
CREATE AVAILABILITY
GROUP MyAG
WITH (DTC_SUPPORT = PER_DB)
FOR DATABASE DB1,
DB2 REPLICA
ON 'Server1' WITH (
ENDPOINT_URL = 'TCP://SERVER1.corp.com:5022',
AVAILABILITY_MODE = SYNCHRONOUS_COMMIT,
FAILOVER_MODE = AUTOMATIC
),
'Server2' WITH (
ENDPOINT_URL = 'TCP://SERVER2.corp.com:5022',
AVAILABILITY_MODE = SYNCHRONOUS_COMMIT,
FAILOVER_MODE = AUTOMATIC
);
Note
上のスクリプトは可用性グループの簡単な例であり、特定の運用環境を想定して設計されてはいません。
分散トランザクション対応に可用性グループを変更する
SQL Server 2017 (14.x) 以降のバージョンでは、分散トランザクション対応に可用性グループを変更できます。 分散トランザクション対応に可用性グループを変更するには、ALTER AVAILABILITY GROUP
スクリプトに DTC_SUPPORT = PER_DB
を追加します。 分散トランザクションをサポートするように可用性グループを変更するスクリプトの例を次に示します。
ALTER AVAILABILITY GROUP MyaAG
SET (DTC_SUPPORT = PER_DB);
Note
SQL Server 2016 (13.x) Service Pack 2 以降のバージョンでは、分散トランザクションの可用性グループを変更できます。 Service Pack 2 より前の SQL Server 2016 (13.x) バージョンの場合、可用性グループを削除し、DTC_SUPPORT = PER_DB
設定で作り直す必要があります。
分散トランザクションを無効にするには、次の Transact-SQL コマンドを使います。
ALTER AVAILABILITY GROUP MyaAG
SET (DTC_SUPPORT = NONE);
分散トランザクション - 技術的概念
分散トランザクションとは、2 つ以上のデータベースにまたがるトランザクションです。 トランザクション マネージャーとしての DTC は、SQL Server インスタンス間、および他のデータ ソース間でトランザクションを調整します。 SQL Server データベース エンジンの各インスタンスは、リソース マネージャーとして動作できます。 可用性グループが DTC_SUPPORT = PER_DB
で構成されていると、データベースはリソース マネージャーとして動作できます。 詳細については、MSDTC のドキュメントを参照してください。
データベース エンジンの 1 つのインスタンスに複数のデータベースが含まれるトランザクションは、実際には分散トランザクションです。 ただし、SQL Server インスタンスは分散トランザクションを内部で処理するため、ユーザーにはローカル トランザクションとして動作しているように見えます。 データベースが DTC_SUPPORT = PER_DB
で構成された可用性グループ内にある場合は、たとえ SQL Server の 1 つのインスタンス内に存在する場合であっても、SQL Server 2017 (14.x) はすべてのクロスデータベース トランザクションを DTC に送ります。
アプリケーションでは、分散トランザクションはローカル トランザクションとほぼ同様に管理されます。 トランザクションの終了時に、アプリケーションがトランザクションのコミットまたはロールバックを要求します。 トランザクション マネージャーが、分散コミットを別で管理することによって、ネットワーク障害により一部のリソース マネージャーがトランザクションを正常にコミットし、その一方で他のリソース マネージャーがトランザクションをロールバックする可能性があるという危険性を最小限に抑える必要があります。 これは、コミット処理を準備フェーズとコミット フェーズの 2 フェーズで管理することによって実現されます。これを 2 フェーズ コミットと呼びます。
準備フェーズ
トランザクション マネージャーはコミット要求を受け取ると、そのトランザクションに関連するすべてのリソース マネージャーに準備コマンドを送ります。 各リソース マネージャーは、トランザクションを持続的にするために必要な処理をすべて実行し、そのトランザクションのログ イメージを含むすべてのバッファーをディスクにフラッシュします。 リソース マネージャーの準備フェーズが完了すると、トランザクション マネージャーに準備フェーズの成否が通知されます。
コミット フェーズ
トランザクション マネージャーは、すべてのリソース マネージャーから準備の正常完了通知を受け取ると、リソース マネージャーにコミット コマンドを送ります。 これにより、リソース マネージャーはコミットを完了できます。 すべてのリソース マネージャーがコミットの正常完了を報告した場合、トランザクション マネージャーは、アプリケーションに成功通知を送ります。 準備できなかったことを報告するリソース マネージャーがあった場合、トランザクション マネージャーはすべてのリソース マネージャーにロールバック コマンドを送り、アプリケーションにコミットできなかったことを報告します。
詳細な手順
以下では、アプリケーションが DTC と連携して分散トランザクションを実行する方法について説明します。
- SQL Server インスタンスは DTC トランザクションに登録します。 これは、トランザクションに 1 つ以上のリソース マネージャーが存在する場合、またはクライアントがトランザクションを DTC トランザクションに昇格するよう要求している場合に行うことができます。
- クライアントは、DTC トランザクション下の SQL Server インスタンスで何らかの処理を行います。
- クライアントは、コミットまたは中止を DTC トランザクションに発行します。
- クライアントが中止を発行した場合、トランザクションはすぐに中止されます。
- クライアントがコミットを発行した場合、DTC は、トランザクションのすべてのリソース マネージャーにトランザクションの準備を要求することによって、2 フェーズ コミット プロトコルを開始します。
- すべてのリソース マネージャーから準備フェーズが正常に行われたことを示す確認応答を受け取った後、DTC はトランザクションをコミットするようすべてのリソース マネージャーに通知します。 何らかの理由で正常な確認応答が妨げられると、DTC はトランザクションを中止します。
分散トランザクション対応に可用性グループを変更することの効果
分散トランザクションに参加している各エンティティは、リソース マネージャーと呼ばれます。 リソース マネージャーの例を次に示します。
- SQL Server のインスタンス。
- 分散トランザクション対応に構成されている可用性グループのデータベース。
- DTC サービスもトランザクション マネージャーになることができます。
- その他のデータ ソース。
分散トランザクションに参加するため、SQL Server のインスタンスは DTC に登録します。 通常、SQL Server のインスタンスはローカル サーバー上の DTC に登録します。 SQL Server の各インスタンスは、一意のリソース マネージャー ID (RMID) でリソース マネージャーを作成し、それを DTC に登録します。 既定の構成では、SQL Server のインスタンスのすべてのデータベースが同じ RMID を使います。
データベースが可用性グループ内にある場合、データベースの読み取り/書き込みコピー (プライマリ レプリカ) は、SQL Server の異なるインスタンスに移動する可能性があります。 この移動中に分散トランザクションをサポートするためには、各データベースが独立したリソース マネージャーとして機能し、一意の RMID を持っている必要があります。 可用性グループで DTC_SUPPORT = PER_DB
が指定されていると、SQL Server はデータベースごとにリソース マネージャーを作成し、一意の RMID を使って DTC に登録します。 この構成では、データベースは DTC トランザクションのリソース マネージャーになります。
重要
DTC では、分散トランザクションあたりの登録は 32 に制限されています。 可用性グループ内の各データベースでは、DTC に個別に登録されるため、トランザクションに 32 を超えるデータベースが含まれている場合は、SQL Server が 33 番目のデータベースを登録しようとすると、次のエラーが発生することがあります:
Enlist operation failed: 0x8004d101(XACT_E_TOOMANY_ENLISTMENTS). SQL Server couldn't register with Microsoft Distributed Transaction Coordinator (MSDTC) as a resource manager for this transaction. The transaction might have been stopped by the client or the resource manager.
SQL Server での分散トランザクションについて詳しくは、「分散トランザクション」をご覧ください。
未解決のトランザクションを管理する
RMID の変更中に存在しているアクティブなトランザクションの結果は、フェールオーバー後に復旧できません。 これは、登録に使われた RMID SQL Server と、復旧に使われる RMID SQL Server が異なるためです。 RMID の変更は、次の場合に発生する可能性があります。
- 可用性グループの
DTC_SUPPORT
を変更する。 - 可用性グループのデータベースを追加または削除する。
- 可用性グループを削除する。
これらの場合、プライマリ レプリカが SQL Server の新しいインスタンスにフェールオーバーすると、インスタンスは DTC に接続してトランザクションの結果を取得しようとします。 データベースが復旧中に未確定トランザクションの結果を取得するために使う RMID が以前に登録されていないため、DTC は結果を返すことができません。 そのため、データベースは SUSPECT 状態になります。
SQL Server の新しいエラー ログには、次の例のようなエントリが含まれます。
Microsoft Distributed Transaction Coordinator (MSDTC)
failed to reenlist citing that the database RMID does
not match the RMID [xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx]
associated with the transaction. Please manually resolve
the transaction.
SQL Server detected a DTC/KTM in-doubt transaction with UOW
{yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy}.Please resolve it
following the guideline for Troubleshooting DTC Transactions.
この例は、DTC がフェールオーバー後に作成されたトランザクションの新しいプライマリ レプリカからデータベースを再登録できなかったことを示します。 SQL Server インスタンスは分散トランザクションの結果を判断できないので、データベースを SUSPECT としてマークします。 トランザクションは作業単位 (UOW) としてマークされ、GUID によって参照されます。 データベースを復旧するには、トランザクションを手動でコミットまたはロールバックします。
警告
トランザクションを手動でコミットまたはロールバックすると、アプリケーションに影響する可能性があります。 コミットまたはロールバックのアクションが、アプリケーションの要件と整合していることを確認してください。
次のスクリプトのいずれか 1 つのみを実行します。
トランザクションをコミットするには、次のスクリプトを更新して実行します。
yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy
を以前のエラー メッセージに含まれる未確定トランザクションの UOW に置き換えて実行します。KILL 'yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy' WITH COMMIT;
トランザクションをロールバックするには、次のスクリプトを更新して実行します。
yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy
を以前のエラー メッセージに含まれる未確定トランザクションの UOW に置き換えて実行します。KILL 'yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy' WITH ROLLBACK;
トランザクションをコミットまたはロールバックした後は、ALTER DATABASE
を使ってデータベースをオンラインに設定できます。 次のスクリプトを更新して実行します。データベース名には、問題のあるデータベースの名前を設定します。
ALTER DATABASE [DB1] SET ONLINE;
未確定トランザクションの解決について詳しくは、「トランザクションを手動で解決する」をご覧ください。