SE の雑記

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

ログ配布の復元時に「ユーザーデータベースを切断する」を有効にしても、排他アクセスが取れなくなるケースがあることの原因について

leave a comment

SQL Server の ログ配布 のリストア時の設定として「バックアップの復元時にデータベースのユーザーを切断する」(disconnect_users=1) という設定があります。

image

この設定は、ログ配布でリストアをしているデータベースに対して「スタンバイ モード」を使用してリストアを行い読み取りが可能なデータベースに対して使用することができるオプションとなります。

読み取りが可能な状態でログ配布を使用している場合、ログをリストアする際にログ配布のプロセス (logship.exe) がデータベースを排他的に利用できない場合、リストアを行うことができないため上記のオプションを設定することでリストアの直前に既存の接続を切断することで、ログ配布が排他的にデータベースを設定することができます。

しかし、この設定を有効にしていてログ配布のプロセスが排他的にデータベースを利用することができず、「エラー: データベースは使用中なので、排他アクセスを獲得できませんでした。RESTORE LOG が異常終了しています。(.Net SqlClient Data Provider)」というエラーが発生するケースがあります。

本投稿では「バックアップの復元時にデータベースのユーザーを切断する」を有効にしていても、リストア時の上述の排他エラーが出る理由についてまとめておきたいと思います。

本事象は「スタンバイモード」を使用する場合に発生するものとなり、「復旧モード無し」にしている場合は、リストア状態のデータベースにアクセスすることはできないため、スタンバイモードを利用していない場合には発生しないはずです。

エラーが発生する原因

「バックアップの復元時にデータベースのユーザーを切断する」を有効にしている場合、トランザクションログバックアップのリストア時には、ログ配布のプロセス (logship.exe) から、次のようなクエリが実行されます。

ALTER DATABASE [<DB 名>] SET SINGLE_USER WITH ROLLBACK IMMEDIATE 

 

これによりシングルユーザーモードに切り替えることで、既存の接続の切断が行われています。

このようなクエリが事前に実行されているにもかかわらず、ログ配布でトランザクションログバックアップのリストアが実行されている最中には、上述の排他アクセスを獲得できなかったエラーが発生するケースがあります。

ログ配布によるトランザクションログバックアップリストア時の処理の特性としては次のような挙動があります。

  1. トランザクションログのバックアップのリストアは 1 ファイルずつ実行される
  2. シングルユーザーモードへの切り替えは、リストア対象のログバックアップの初回リストア時に「のみ」実行される
    • 1 個めのバックアップのリストアを実行する前に既存の接続がすべて切断され、シングルユーザーモードに切り替わる
    • 2 個めのファイルをリストアする際には、明示的なシングルユーザーモードへの切り替えは実行されない
  3. ログのバックアップがリストアされるとマルチユーザーモードに自動的に切り替わる

 

トランザクションログのリストア時にシングルユーザーモードに切り替わっているのは最初のログバックアップのリストアが完了するまでで、2 個目以降のトランザクションログのバックアップをリストアするタイミングでは、マルチユーザーモードの切り替わった状態でリストアが行われます。

そのため、2 個目以降のバックアップをリストアしている最中には、処理しているファイルが切り替わるタイミングで、マルチユーザーとなっているタイミングが存在しており、このタイミングでリストアをしているデータベースに対してアクセスが行われると、アクセスができてしまい、排他アクセスのエラーが発生する要因となりえます。

現状、各ファイルのリストア時に毎回シングルユーザーモードに切り替えるという動作を行うことはできないため、複数のログバックアップファイルをリストアしている際に、シングルユーザーモードを維持し続けるということはできません。

ログ配布によるログリストアのコマンドタイムアウトは 10 数秒 (私が確認した範囲では 20 秒程度は待機しているようでした) あるようですので、どのクエリが原因となり、排他アクセスが取得できなかったかの調査については、Blocked Process Report イベント クラス の設定を行うことで確認はできます。

 

エラーの回避方法

2 個目以降のバックアップファイルをリストアしている最中に、排他アクセスが取得できない場合にはリストアが失敗し、ログ配布がエラーとなります。

このエラーの回避方法ですが 2 つの案が考えられるかと思います。

  1. 次回のログバックアップのリストアタイミングでエラーが発生したファイルのリストアを再実行させる
  2. ログバックアップのリストアのジョブステップの再試行回数を 0 回から変更する

 

基本的な方針としては「1.」でよいかと思います。

排他アクセスを獲得できず、ログバックアップのリストアがエラーとなった場合でも、次回のスケジュールでログのリストアがされるタイミングでエラーとなったログファイルのリストアも実行されますので、頻繁にログのリストアを実行しているのであれば、ファイルの削除が行われる前に次回のリストア処理が実行され再処理が行われますので、自動的に復旧されます。

それ以外の方法としては、「2.」リストアのジョブステップの再試行回数を変更する方法が考えられます。

初期設定ではリストアのジョブステップの再試行回数は「0」に設定されており、エラーが発生しても処理の再試行は行われません。

image

この再試行回数を変更することで、次回のリストアジョブが実行される前に自動的に再試行が行われ処理のリカバリを行うことができます。

再試行時には再度シングルユーザーモードへの切り替えが行われるため、既存の接続の切断も行われますので、排他アクセスの取得を阻害していたセッションについても再試行時に切断が行われます。

基本的には「1.」の次回リストア時の再試行で問題ないと思いますが、リストアをするスケジュールが 1 日に 1 回というようなリストア間隔が長い場合などに、次回リストアを実行する前に再試行をしておきたいという場合は「2.」を設定することになるのではないでしょうか。

 

ログ配布先のデータベースを「スタンバイモード」で利用できる状態にしておくことで、読み取りワークロードが可能なデータベースとして利用することが可能です。

しかし、本投稿に記載したような排他アクセスが取得できないタイミングが存在し得るため、排他アクセスのエラーが出た場合にはリトライをどのように実行するかを検討しておくとよいのではないでしょうか。

頻繁に排他アクセスのエラーが発生する場合は、Blocked Process Report イベント クラス の取得を設定しておき、ユーザークエリなのかシステムクエリなのかの確認をしてみるのも良いかと思います。

Share

Written by Masayuki.Ozawa

1月 8th, 2024 at 11:41 pm

Posted in SQL Server

Tagged with

Leave a Reply