AlwaysOn 可用性グループでは最大 3 台で同期コミットモードを構築することができます。
同期コミットモードではプライマリレプリカでデータの変更をした際のコミット時に、セカンダリレプリカでのコミットを保証するために、セカンダリでコミットが行われるのを確認してから処理を完了させるという動作になります。
複数台で同期コミットモードを構築した場合、コミットの完了がどのように動作するのかを見ていきたいと思います。
■3 台で同期コミットモードを構築した場合のコミットの同期について
単純に構築しただけだと同期コミットによるコミットの遅延の影響が確認しにくいため、以下のような構成をとっています。
2 台のセカンダリレプリカのうち、1 台はネットワークの遅延はない状態、もう 1 台は SW ルーター (Vyatta) を間に挟み、ネットワークの遅延を発生できる状態にしています。
可用性グループは以下のように設定をしてあり、CLS-NODE-03 が SW ルーターを経由してアクセスが行われるサーバーとなります。
帯域制御が設定されていない場合、可用性データベースに対して更新を行うと、数ミリ秒で結果が返ってきます。
それではプライマリ (CLS-SQL-FCI) → からセカンダリ (CLS-NODE-03) の間に 1000ms (1 秒) の遅延を設定して、更新を行ってみます。
先ほどは 3 ミリ秒で結果が返ってきていたクエリが、1004 ミリ秒かかるようになり、実行が完了するまでにネットワークの遅延の影響が出てきたことが確認できます。
同期コミットを設定している場合、同期コミットが設定されている全サーバーでコミットが完了し、その ACK が返ってくることで処理が完了します。
そのため、同期コミット設定サーバーのどれか 1 台へのコミット – 応答に遅延がある場合、トランザクション全体が待たされることになります。
今回の場合、
- CLS-SQL-FCI → CLS-NODE-03 : 遅延なし
- CLS-SQL-FCI → CLS-NODE-04 : 遅延あり
となっていますので、CLS-NODE-04 へのネットワーク遅延が影響し、CLS-SQL-FCI のトランザクションに対して恒常的に遅延が発生していることになります。
このような、同期コミットを使用した場合のコミットの同期による待ち状態ですが、プライマリレプリカの [HADR_SYNC_COMMIT] として現れてきます。
遅延の設定をしていない場合の [HADR_SYNC_COMMIT] の待ち事象の状況は以下のようになります。
1 回の INSERT のみを実行した状態ですので、その INSERT でコミットの同期に 4ms かかったことが確認できます。
先ほどは 4ms だったものが 1004ms になっていることが確認できます。
同期コミットによる待ちはプライマリレプリカの情報としてあらわれてくるため、同期コミットを設定した場合の遅延に関してはこの項目を確認することで情報を取得することができます。
# バッチ処理とショートトランザクションでは傾向が変わってくるのでこのあたりの処理の違いは意識する必要がありますが。
遅延を発生させた状態でルーター越しに配置している CLS-NODE-03 の SQL Server のサービスを停止した状態にしてみます。
この状態で INSERT を実行した場合は、以下のようになります。
同期コミットは起動している可用性レプリカに対してコミットを同期しますので、起動していないレプリカに対してはコミットの同期が発生しないため、ルーター越しのサーバーが起動していない状態では遅延による影響が発生しないようになっています。
# プライマリレプリカではセカンダリレプリカの接続状況を認識しているため、接続されていないレプリカに対しての影響は出ないようです。
この時の [HADR_SYNC_COMMIT] の待ち事象ですが、以下のように集計されています。
[HADR_SYNC_COMMIT] は各サーバーへの待ち事象を個別に集計するのではなく、トランザクション内での合計の待ち時間として集計がされています。
そのため、
- CLS-SQL-FCI → CLS-NODE-03
- CLS-SQL-FCI → CLS-NODE-04
という同期コミットのセカンダリレプリカがある場合と、
- CLS-SQL-FCI → CLS-NODE-04
という同期コミットのセカンダリレプリカがある場合ではどちらも単一のトランザクションに対しての待ち事象の回数としては 1 としてカウントされます。
日中のバッチ処理が発生しない時間の [HADR_SYNC_COMMIT] の[waiting_tasks_count] と [wait_time_ms] を把握しておくことでトランザクションあたりの平均同期待ち時間を把握するということもできそうですね。
# バッチ処理のような単一のトランザクションで大量のデータを変更する場合は I/O パターンの考え方が少し変わってきますので、待ち事象を用いた平均同期待ちはショートトランザクションのみが実行されている時間で取得するのが良いと思います。
同期コミットを設定している場合、セカンダリレプリカとのコミットの同期が処理効率に大きく影響が出てきます。
ネットワークの遅延がある環境に対して同期コミットのレプリカを配置するということはあまりないと思いますが、同期コミットを使用する場合は遅延の影響をダイレクトに受けてくるということを認識しておくとよさそうですね。
更新に遅延が発生すると、その分ロックを取得している時間も長くなりますので分離レベルによっては更新処理が長くなる影響によって参照処理にも影響が出てきますので。
# 排他ロックを取得している時間が長くなることで、参照時の共有ロックの取得待ちに影響が出ることが考えられます。