SQL Server ベースの環境でロックのメモリが大量に確保されてしまった場合に、どのようにすれば解放できるかについての投稿となります。
内容としては次の KB に記載されているものとなります。
SQL Server ではロックエスカレーションの概念があるため、通常、大量のロックを取得しようとした場合はテーブルロック (オブジェクトロック) にエスカレーションすることで、ロックで使用するメモリを削減する動作が行われます。
しかし次のようなケースではロックエスカレーションできないケースがあります。
- トレースフラグ 1211 / 1224 でロックエスカレーションを無効にする
- ロックエスカレーションをしようとした場合に、ロック競合が発生しエスカレーションに失敗する
- テーブルでロックエスカレーションを無効にしている
このようなケースに合致した場合、ロックエスカレーションを行うことができず、細かなロックを取得する動作で、実行が継続されることがあります。
SQL Server では、ロック辺りのサイズは 96 バイトと小さなサイズではありますが、ロックエスカレーションができず、大量にロックが取得された場合などは、ロックのメモリが GB 単位で取得されることがあります。
実際に大量にロックを取得した状態のメモリ使用量が以下となります。
SQL Server で 55 GB のメモリが割り当てられていますが、その中でロックで 33 GB のメモリが使用されています。
ここで取得されたメモリは時間経過とともにバックグラウンドタスクで解放はされるのですが、SQL Server の実行状況によっては解放の速度が遅いケースがあるようです。
このような状態になり、「手動でロックのメモリを解放したい」というような場合には、DBCC FREESYSTEMCACHE を使用することができます。
ドキュメントでは次の構文が提示されています。
DBCC FREESYSTEMCACHE
( ‘ALL’ [ , pool_name ] )
[ WITH
{ [ MARK_IN_USE_FOR_REMOVAL ] , [ NO_INFOMSGS ] }
]
ドキュメントでは、コマンドの第一引数は「ALL」のみ提示されており、この指定でクエリを実行することでロックのメモリも解放されるのですが、それ以外の領域についても解放がされてしまい、影響範囲が大きくなってしまいます。
この箇所の指定ですが、解放するメモリクラークの名称を指定することができ、ロックの領域をピンポイントで指定することができます。
ロックの解放を行い場合には、次のクエリでメモリクラークの名称を取得します。
SELECT * FROM sys.dm_os_memory_clerks WHERE name LIKE 'Lock Manager%'
今回のケースであれば「Lock Manager : Node 0」が大量にメモリを確保していることが確認できます。
この領域の使用の領域を解放するのであれば、次のクエリを実行します。
DBCC FREESYSTEMCACHE('Lock Manager : Node 0')
DBCC FREESYSTEMCACHE を実行していますが、先ほどと異なり、ロックについての領域を指定していますので、この領域のみクリア (フリーリスト化) されることになります。
通常、ロックのメモリのみ使用部分は自動的に解放されるため、このような手法をとらなくてもよいはずです。
しかし、何らかのワークロードに起因して、ロックのメモリがなかなか解放されない場合には、このような方法でロックのメモリをピンポイントで解放する方法が提供されていることを把握しておくとよいのではないでしょうか。