SQL Server 2022 / SQL Database / Managed Instance では、データベースのファイルサイズを削減するためのコマンドである、DBCC SHRINKFILE に WAIT_AT_LOWPRIORITY というオプションが追加されました。
このオプションを使用した場合のデータファイルのサイズ縮小について動作を把握しておきたかったので、調べてみました。
関連ドキュメント
ドキュメントとして確認をしておきたいものが次の内容となります。
WAIT_AT_LOW_PRIORITY で緩和できるロック競合
WAIT_AT_LOW_PRIORITY は DBCC SHRINKFILE によるデータファイルのファイルサイズの物理的な縮小を実施する際に、低優先度ロックを取得することで、他のセッションの処理がブロックされないようにする機能となります。
DBCC SHRINKFILE のすべての処理で低優先度のロックを取得するかというとそういうわけではなく、コンカレンシーの問題に記載されている次のロックについて低優先度での取得を行うものとなります。
現在、Index Allocation Map (IAM) ページを移動または削除するときにスキーマ変更ロック (Sch-M) が必要になり、ユーザー クエリで必要な Sch-S ロックがブロックされます。
DBCC SHRINKFILE でデータの移動を伴うデータファイルの縮小 (TRUNCATEONLY を使用しない実行) については、データファイルの後方のページを前方の未使用領域に移動し、データファイルの物理的な後方に空きを作ることで、データファイルの未使用領域を削除するという動作になります。
この処理を行う際には様々なロックが取得されるのですが、低優先度で取得が行われるのは上記の引用の IAM の移動 / 削除を実行する場合の SCH-M のロックのみとなります。
IAM の移動が発生する場合、該当の IAM で管理されているオブジェクトに対して瞬間的に Sch-M のロックが取得されます。
実際に DBCC SHRINKFILE で IAM の移動が発生する場合のトランザクションログの内容が以下になるのですが、IAM の操作で Sch-M が取得されていることが確認できます。
Sch-M のロックは様々なロックをブロックするため、同時実行性が著しく低下してしまいますが、低優先度 (LOW_PRIORITY_WAIT) で取得した Sch-M は他のセッションのロックより優先度は低いため、この Sch-M によって Sch-S のロックの競合が発生しなくなります。
WAIT_AT_LOW_PRIORITY モードの新しい縮小操作が実行時間の長いクエリのためにロックを取得できない場合、縮小操作は最終的に既定で 1 分後にタイムアウトし、通知せずに終了します。
低優先度の Sch-M のロックについては、1 分間でタイムアウトするようになっているため、DBCC SHRINKFILE が Sch-M のロックが取得できない状態については、この時間を経過するとタイムアウトにより、SHRINKFILE がエラーとなるようになります。
IAM が移動 / 削除が発生するかについては、ファイル内の移動対象がどの程度あるかによって変わるので、発生を抑止することはできないという認識ですが、IAM の移動が発生した際の同時実行性の低下は WAIT_AT_LOW_PRIORITY を指定することで緩和させることができます。
WAIT_AT_LOW_PRIORITY で緩和できないロック競合
オプションを指定しても DBCC SHRINKFILE によるデータファイル削除の処理で、すべてのロック競合が緩和できるわけではありません。
前述のとおり、DBCC SHRINKFILE は後方のデータを前方の未使用領域に移動することで、空き領域を作り出しファイルの縮小を行う処理となります。
この「データの移動」は「ページ単位」で実施されているようなのですが、移動時には瞬間的に「PAGE:X」のロックが取得されます。
このロックについては WAIT_AT_LOW_PRIORITY の対象にはなりません。
そのため、検索系の処理等で移動対象のページに「PAGE:S / PAGE:IS」のようなロックが取得されている場合、ロック競合が発生し、ページの移動がブロックされます。
SHRINK がブロックされている PAGE に対してのロックが短時間で解放されるのであれば、瞬間的な「PAGE:X」の取得は問題がない可能性が高いですが、そうでない場合には、多数のセッションのブロッキングを誘発するヘッドブロッカーになる可能性があります。
空き領域を確保するためのページ移動時のロック競合については、WAIT_AT_LOW_PRIORITY では防ぐことができないのは意識しておく必要があるのではないでしょうか。