SE の雑記

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

SQL Server 2025 の最適化されたロックの参考情報

leave a comment

SQL Server 2025 の データベースエンジンの新機能 として「最適化されたロック (Optimized locking)」という機能があります。

最適化されたロックにより、ロックのブロッキングとロック数の減少によるメモリ消費量の削減が期待できます。

詳細な挙動については理解をしている最中ですが、どのドキュメントを確認すればよいのかについてまとめておきたいと思います。

最適化されたロックに関するドキュメント

最適化されたロックのドキュメント

最適化されたロックの挙動を確認する際には次のドキュメントを確認します。

最適化されたロックは Azure SQL Database で先行して実装されていた機能となりますので、機能提供時のドキュメントも挙動を把握する際には参考となります。

最適化されたロックは「トランザクション ID (Transaction ID: TID) ロック」「修飾後のロック (Lock after qualification: LAQ)」という二つのコンポーネントで構成されています。

最適化されたロックは、トランザクション ID (TID) のロック修飾後ロック (LAQ) の 2 つの主要コンポーネントで構成されます。

  • トランザクション ID (TID) は、トランザクションの一意の識別子です。 各行には、最後に変更された TID のラベルが付けられます。 キーまたは行識別子のロックが多くなる可能性がある代わりに、TID に対して 1 つのロックが使用されます。 詳細については、「トランザクション ID (TID) ロック」を参照してください。
  • 修飾後のロック (LAQ) は、ロックを取得せずに、最新のコミット済みバージョンの行に対するクエリの述語を評価する最適化であり、コンカレンシーを向上させます。 詳細については、「修飾後のロック (LAQ)」を参照してください。

 

デモ用のコンテンツ

最適化されたロック にデモ用のコンテンツも含まれていますので、最初の動作確認については、ドキュメントの内容で実施することができます。

Bob Ward が Optimized locking SQL Server 2025 でデモ用のコンテンツを公開していますので、

 

既存ドキュメントの確認

新規のドキュメントだけでなく、既存のロックのドキュメントにも更新が行われています。

最適化されたロックを使用するには「高速データベース復旧 (Accelerated database recovery: ADR)」を有効化する必要があります。

LAQ を使用するためには、「行のバージョン管理」を有効化する必要があります。

新機能を使用する場合には、これらの既存の機能についても挙動を把握しておく必要があります。

 

最適化されたロック: トランザクション ID (TID) ロック

最適化されたロックの有効化

ADR が有効になっているデータベースで、次のステートメントを実行することで、最適化されたロックを有効化することができます。

ALTER DATABASE [<DB Name>] SET OPTIMIZED_LOCKING = ON

 

SQL Database では、ADR はデフォルトで有効になっていますが、SQL Server 2025 ではデフォルトでは無効な状態となっていますので、ADR が無効な場合は、最初に ADR の有効化を実施してから、最適化されたロックを有効にします。

ALTER DATABASE [<DB Name>] SET ACCELERATED_DATABASE_RECOVERY = ON
ALTER DATABASE [<DB Name>] SET OPTIMIZED_LOCKING = ON

 

これにより最適化されたロックが使用可能となり、一部のワークロードで「トランザクション ID (TID) ロック (XACT ロック)」が取得されるようになります。

 

基本的な挙動

次のような UPDATE を実行したとします。

UPDATE t0 SET b = b + 10 WHERE a=1;
UPDATE t0 SET b = b + 10 WHERE a=2;

 

従来までのロック機構では、変更対象のデータとページに対してロックが取得され、更新が完了するまではこれらのデータに対してのアクセスはブロッキングされます。

image

 

最適化されたロックが有効な場合、同様の UPDATE を実行した場合、次のようなロック取得となります。

image

従来までは更新対象の各データについてロックを取得していましたが、最適化されたロックでは、「XACT (トランザクション) ロック」の取得となっています。

最適化されたロックが有効な場合の、XACT ロックは、更新対象のデータ行のバージョニングのトランザクションタイムスタンプ (1452) に対してロックが取得されています。

image

この対象のデータに対して他のセッションがアクセスするとブロッキングが発生しますが、XACT ロックの競合が発生した状態となります。

image

リソースの詳細 から XACT ロックの resource_description のフォーマットを確認することができます。

image

今回のケースであれば、XACT / XACT キーでロック競合が発生した状態になっているようですね。

sys.dm_os_wait_stats にもロック待機の情報が追加されており、「LCK_M_S_XACT」といったような待機事象が追加されています。

image

該当のデータを他のセッションから操作する際には、「該当のデータにロックが取得されているか?」ではなく、「該当のデータのトランザクション ID に対してロックを取得できるか?」で、排他制御を行うというような挙動となるようです。

 

最適化されたロック: 修飾後ロック (LAQ)

修飾後ロック (LAQ) については最適化されたロックの他に、「READ COMMITTED SNAPSHOT ISOLATION (RCSI)」を有効化しておく必要があります。

ALTER DATABASE [<DB Name>] SET READ_COMMITTED_SNAPSHOT ON;
ALTER DATABASE [<DB Name>] SET OPTIMIZED_LOCKING = ON;

 

これにより LAQ が動作するようになります。

LAQ の動作を確認する際には、最適化されたロックと RCSI によるクエリ動作変更 のクエリで確認ができます。

次のクエリを複数のセッションで実行することで動作を確認しています。

BEGIN TRANSACTION T1;

UPDATE t4

SET b = 2

WHERE a = 1;

ROLLBACK TRANSACTION;   

BEGIN TRANSACTION T2;

UPDATE t4

SET b = 3

WHERE b = 2;

ROLLBACK TRANSACTION;

 

最初のセッションでは、「a=1」のデータを「b=1 -> 2」に更新を行います。2 つめのセッションでは「b=2 -> 3」 に更新しており、最初のセッションの更新後のデータに対して更新を行うクエリとなります。

通常のデータベースの状態 (RCSI=Off or On / 最適化されたロック=Off) の状態では、RID に対してロック競合が発生しています。

image

最適化されたロックを有効化した場合は次のロック競合が発生します。

image

最適化されたロックを使用していても、このケースでは XACT ロックの競合が発生しています。

LAQ を有効 (RCSI = On / 最適化されたロック = On) にするとロック競合が発生せずに処理を実行できるようになります。

最初のセッションでは、従来通り XACT ロックが取得されます。

image

しかしこれまでのクエリと異なり、2 爪のセッションではブロッキングは発生せずに、0 件の更新として処理が完了します。

これは、次の挙動によるためです。

LAQ では、トランザクション T2 は、列 b1 と等しい行の最新のコミット済みバージョンを使用して、述語 (b = 2) を評価します。 行は適格でないため、スキップされ、トランザクション T1 によってブロックされることなくステートメントが完了します。 この例では、LAQ はブロッキングを削除しますが、結果が異なります。

2 つ目のセッションでアクセスされる情報は RCSI が有効であるため、「b=2」がコミット済みのデータを対象としてアクセスが行われます。最初のセッションでは「b=2」のコミットは行われていないため、コミット済みのデータとしては「b=1」となります。

LAQ ではコミット済みのデータに対して述語 (b=2) で処理対象データを評価する動作となるため、未コミットのデータである「b=2」に対してはロック競合が発生せずにアクセスが行われるという挙動になるようです。

コミット済みデータに対して、クエリの述語を使用してアクセスを行うという挙動となるため、未コミットの状態で述語を満たすデータがあったとしてもアクセスは行われずロック競合が発生しないという仕組みのようです。

LAQ ヒューリスティック に記載されていますが、LAQ は一部ののステートメントで abort (lock_After_qual_stmt_Abort) した場合、LAQ 無しで再処理が行われるというように内部的な再起動のオーバーヘッドが発生し、再起動されたステートメントが多い場合には自動的に無効化される挙動となるようです。

(再度しきい値を下回った場合は、有効化される。LAQ の再起動の状況は状況は、locking_stats 拡張イベントで確認ができる。)

RCSI を使用して、行バージョニングにより参照のロック競合を緩和させるというアプローチが従来の内容でしたが、更新 (INSERT / UPDATE / DELETE) についても、RCSI を活用することでロック競合を緩和させるというアプローチが LAQ なのかと思います。

 

本機能により従来より取得されるロック数が減少し、ロック競合を抑えられるワークロードが増える可能性がありますが、取得されるロックが変わってきますので、新しい設定によりどのような挙動になるのかは意識しておきたいですね。

Share

Written by Masayuki.Ozawa

8月 11th, 2025 at 10:37 pm

Leave a Reply