SE の雑記

SQL Server の情報をメインに Microsoft 製品の勉強内容を日々投稿

非同期モードの可用性グループのフェールオーバーとデータ損失について

leave a comment

本投稿は 8/28 に大幅に修正しました。

AlwaysOn 可用性グループの非同期モードはプライマリレプリカの更新をセカンダリレプリカに反映する前にトランザクションを完了させるため、トランザクション量に応じてプライマリレプリカとセカンダリレプリカ間でデータの差異が発生する可能性があります。

今回の投稿では非同期モードを使用している場合のフェールオーバーについてまとめてみたいと思います。

■非同期モードのフェールオーバーについて


非同期モードでもプライマリレプリカとセカンダリレプリカ間でフェールオーバーをすることができますが、強制フェールオーバーのみ使用することができます。
image

強制フェールオーバーはフェールオーバー後にデータ損失の可能性があるフェールオーバーになります。

データ損失が発生するケースとしては

  • プライマリレプリカの更新内容がセカンダリレプリカに反映されていない

場合が想定されます。

このケースが発生する条件としては、

  • セカンダリレプリカが停止していてプライマリレプリカの内容が反映できていない
  • ネットワークの帯域やスペックが不足していてセカンダリレプリカへの送信キューが蓄積している

という状態が考えられます。
# REDO キューが蓄積されている状態は、セカンダリレプリカが受信した情報を反映するためのキューですので、この状態だけであれば、プライマリレプリカの更新はセカンダリレプリカの反映されています。

データ同期に遅延が発生していない、参照系の処理が頻繁に発生している状態であればプライマリレプリカとセカンダリレプリカのデータは非同期モードを使用していても差がない状態となります。

それではデータの差がない状態でフェールオーバーを実施してみたいと思います。
非同期モードですのでデータ損失の可能性のある強制フェールオーバーが実行されます。

強制フェールオーバー後は、可用性データベースの状態は中断状態 (データの同期が中断されている状態) となっています。
image
image

[データ移動の再開] をすることで同期を再開することができます。
image
image

 

■データ損失を伴うフェールオーバー


それでは、強制フェールオーバー時にセカンダリレプリカのデータがプライマリレプリカの最新の状態を受信できていない場合を考えてみます。
非同期モードの場合、プライマリレプリカとセカンダリレプリカでトランザクションの完了を同期することなく処理が完了します。
そのため、送信キューにデータがたまっているような状態では一部のデータがセカンダリレプリカに反映されていないことになります。
この場合、トランザクションログの内容は以下のようになります。
image

可用性グループでは更新はプライマリレプリカでのみ可能です。
そのため、最新のトランザクションログのレコードは A になります。

セカンダリレプリカで同期の遅延が発生している場合、セカンダリレプリカのトランザクションログは最新化されていません。
そのため、遅延が発生している状況ではセカンダリレプリカのトランザクションログは B の時点までしか動機がされていないことになります。

この A ~ B のトランザクションログの差がログ送信キューとして滞留しているログであり、データ損失する内容となります。

SSMS の可用性ダッシュボードを使用することログ送信キューや推定データ損失の情報を取得することができます。
# デフォルトでは表示されていませんので列を追加する必要があります。
これらの列に情報が表示されている場合は、フェールオーバーをした場合にデータの損失が発生する可能性があるということになります。

image

この画像では情報が表示されているため、フェールオーバーが発生すると情報の損失が発生する可能性があることを示しています。
この状態で非同期モードのセカンダリレプリカに強制フェールオーバーしてみます。
強制フェールオーバー直後はフェールオーバー前のプライマリレプリカは未同期 (同期が開始されていない) の状態となっています。
image

この時の同期状況を以下のようなクエリで取得してみます。

SELECT
  adc.database_name
  , drs.synchronization_health_desc
  , nip.ag_name
  , ar.replica_server_name
  , drs.last_commit_lsn
  , drs.last_commit_time
  , drs.last_redone_lsn
  , drs.last_redone_time
  , drs.recovery_lsn
FROM
  sys.dm_hadr_database_replica_states AS drs
LEFT JOIN
  sys.dm_hadr_name_id_map AS nip
ON
  nip.ag_id = drs.group_id
LEFT JOIN
  sys.availability_replicas AS ar
ON
  ar.replica_id = drs.replica_id
LEFT JOIN
  sys.availability_databases_cluster AS adc
ON
  adc.group_database_id = drs.group_database_id
ORDER BY
  ar.replica_server_name
  , drs.database_id

image

CLS-NODE-03 がプライマリレプリカ、CLS-SQL-FCI がセカンダリレプリカとなります。
[last_commit_time] からプライマリレプリカでは、[2012/8/27 22:15:16.183] までの変更が反映されていることが確認できます。
[last_redone_time] からセカンダリレプリカでは、[2012/8/27 22:17:11.287] まで更新が行われていたことが確認できます。
つまり、この差が強制フェールオーバーをしたことによるデータ損失の情報となります。

トランザクションログの内容は [sys.fn_dblog] で確認することができます。
プライマリレプリカのログを確認すると、[2012/8/27 22:17:11.287] 付近のログは含まれていないことが確認できます。
# 一番下までスクロールした状態が以下の画像になります。
image

この状態でセカンダリレプリカの同期を再開してみます。
可用性データベースのアイコンは×の状態となり、データベースは [同期されていません / 復旧中] となります。
# データベースの状態を確認するタイミングによっては [元に戻しています / 復旧中] になっているかもしれません。
image

復旧が完了すると、データの同期が再開されます。
image

復旧中に何をやっているかというと、トランザクションログのロールバックが実行されているようです。
セカンダリレプリカのトランザクションログはプライマリレプリカと比較して進んでいるため、プライマリレプリカと整合性が取れるようにロールバックする必要があります。
このための処理をロールバックとして実行しているようです。
image

イベントビューアーを確認すると同期を再開したインスタンスで以下のようなログが出力されます。
imageimage

この時表示されている [復旧 LSN] が sys.dm_hadr_database_replica_states の [recovery_lsn] の値となるようです。

差分が多ければ多いほど、ロールバックに必要となる時間が長くなります。

強制フェールオーバー後にセカンダリレプリカをバックアップからのリストア等で、
image
というような状態になり、データの同期を再開した場合はセカンダリレプリカにログの転送が行われるようです。

■データ同期の再開ができなかった場合の対応


障害の発生状況によってはデータ同期を再開できないことがあるかもしれません。
そのような場合は、データ移動を再開することはできませんのでデータ同期を再開するためには以下の手順を実施し、再度同期の設定をする必要があります。

  1. プライマリレプリカで可用性グループのプロパティを開き、可用性レプリカからセカンダリレプリカを削除
    image
  2. セカンダリレプリカで可用性グループが削除されることを確認
    # 解決中の状態となってしばらくするとセカンダリレプリカから可用性グループの情報が削除されます。解決中のままなかなか削除されない場合はセカンダリレプリカで SQL Server のサービスを再起動すると起動後に削除されるかと思います。
  3. プライマリレプリカでデータベースの完全バックアップを取得
  4. プライマリレプリカでトランザクションログのバックアップを取得
  5. セカンダリレプリカでデータベースを削除
  6. セカンダリレプリカで取得したデータベース / トランザクションログのバックアップを WITH NORECOVERY でリストア
    image
  7. プライマリレプリカの可用性グループでレプリカの追加を実施
    image
  8. すでにバックアップをリストア済みのため、[結合のみ] でレプリカの追加を行う
    image

このような同期の再設定の手順を実施することで、再度同期を開始することができます。
image

データベースの状態によっては少し手順が変わることもあるのですが…。

同期の再設定をする際の重要なポイントとしては、可用性グループの削除はプライマリ / セカンダリレプリカのどちらでも実施しない という点になるかと。
セカンダリレプリカでも可用性グループの削除は実施できるのですが、削除をしてしまうとプライマリレプリカ上でも可用性グループの情報が削除されます。
これを回避するために、セカンダリレプリカで可用性グループを無効にするためには、レプリカから削除をするという操作をするのが良いと思います。
# レプリカを削除してからであればセカンダリレプリカ上で可用性グループを削除を実行してもプライマリレプリカには影響がないはずです。

 

強制フェールオーバーを実施するのは、障害等で同期がとれていない状態でもやむを得ずセカンダリレプリカで起動しなくてはいけない時だけだと思います。
通常、非同期モードを使用していてフェールオーバーを実施する場合は、強制フェールオーバーではなく一度同期モードに変更をしてデータ損失のない状態でフェールオーバーを実施するのが望ましいと思います。

以下のような運用が可能な手順を考慮しておくのがポイントでしょうか。

  • フェールオーバーは一時的に同期モードにして実行する
  • 強制フェールオーバーを実施する場合、同期の再設定が必要な場合があることを考慮しておく
  • 同期を再設定する場合、可用性グループを残した状態で再同期ができるような手順を確立しておく

非同期モードでもうまく動くものですね~。

Written by masayuki.ozawa

8月 24th, 2012 at 12:37 am

Posted in SQL Server

Tagged with

Leave a Reply

*