最近、コミュニティの誰かから CLR_MANUAL_EVENTに関する質問メールを受け取りました。 待機タイプ;具体的には、SQL Serverの空間メソッドを使用して、空間データ型とクエリに大きく依存する既存のワークロードで、この待機の問題をトラブルシューティングする方法が突然一般的になりました。
コンサルタントとしての私の最初の質問は、ほとんどの場合、「何が変わったのか」です。しかし、この場合、多くの場合と同様に、アプリケーションのコードやワークロードのパターンに何の変化もなかったと確信しました。そこで、最初に立ち寄ったのは、 CLR_MANUAL_EVENTをプルアップすることでした。 SQL Serverで問題が発生するのは一般的に待機ではないため、SQLskills.com待機タイプライブラリで待機して、この待機タイプに関してすでに収集した他の情報を確認してください。私が本当に興味深いと思ったのは、ページの上部にあるSentryOneによって提供されたこの待機タイプの発生のチャート/ヒートマップでした:
このタイプのデータが顧客の良好な断面全体で収集されていないという事実は、これが一般的な問題ではないことを私に本当に確認したので、この特定のワークロードが現在表示されているという事実に興味をそそられましたこの待機に関する問題。問題をさらに調査するためにどこに行けばよいかわからなかったので、文字通り何十ものスレッドが空間クエリを実行する原因がわからなかったため、これ以上助けることができず申し訳ありませんでしたとメールに返信しましたこの待機タイプでは、突然2〜4秒待機する必要があります。
翌日、問題を解決したことを知らせる質問をした人から、親切なフォローアップメールを受け取りました。実際、実際のアプリケーションワークロードには何も変更されていませんが、発生した環境に変更がありました。サードパーティのソフトウェアパッケージがセキュリティチームによってインフラストラクチャ内のすべてのサーバーにインストールされ、このソフトウェアは5分間隔でデータを収集し、.NETガベージコレクション処理を非常に積極的に実行し、彼らは言った。この情報と.NET開発に関する過去の知識を武器に、これを試して、動作を再現できるかどうか、および原因のトラブルシューティングをさらに進める方法を確認したいと思いました。
背景情報
何年もの間、私は常にMSDNのPSSQLブログをフォローしてきました。これは、過去のある時点でSQL Serverに関連する問題について読んだことを思い出すと、通常、私の行きつけの場所の1つです。すべての詳細を覚えておいてください。
CLR_MANUAL_EVENTとCLR_AUTO_EVENTの待機時間が多いというタイトルのブログ投稿があります。 2008年のJackLiによる、これらの待機が集約で安全に無視できる理由を説明しています sys.dm_os_wait_stats DMVは通常の状態で待機が発生するためですが、待機時間が長すぎる場合の対処方法や、 sys.dm_os_waiting_tasksの複数のスレッドで待機時間が発生する原因については説明していません。 積極的に。
2013年のJackLiによる別のブログ投稿があります。CLRガベージコレクションとSQLCPUアフィニティ設定に関連するパフォーマンスの問題 複数のインスタンスに関する考慮事項と、1つのインスタンスによってトリガーされる.NETガベージコレクター(GC)が同じサーバー上の他のインスタンスにどのように影響するかについて話すときに、IEPTO2パフォーマンスチューニングクラスで参照します。
.NETのGCは、オブジェクトに割り当てられたメモリを自動的にクリーンアップできるようにすることで、CLRを使用するアプリケーションのメモリ使用量を削減するために存在します。これにより、開発者は、管理されていないコードで必要な程度にメモリの割り当てと割り当て解除を手動で処理する必要がなくなります。 。 GCの機能について詳しく知りたい場合は、Books Onlineに記載されていますが、コレクションがブロックされる可能性があるという事実以外の詳細は、 CLR_MANUAL_EVENTでのアクティブな待機のトラブルシューティングには重要ではありません。 さらにSQLServerで。
問題の根源にたどり着く
.NETによるガベージコレクションが問題の原因であることがわかったので、 AdventureWorks2016に対して単一の空間クエリを使用して実験を行うことにしました。 非常に単純なPowerShellスクリプトを使用して、ループ内でガベージコレクターを手動で呼び出し、 sys.dm_os_waiting_tasksで何が発生したかを追跡します。 クエリ用のSQLServerの内部:
USE AdventureWorks2016; GO SELECT a.SpatialLocation.ToString(), a.City, b.SpatialLocation.ToString(), b.City FROM Person.Address AS a INNER JOIN Person.Address AS b ON a.SpatialLocation.STDistance(b.SpatialLocation) <= 100 ORDER BY a.SpatialLocation.STDistance(b.SpatialLocation);
このクエリは、 Person.Address内のすべてのアドレスを比較しています。 テーブル内の他のアドレスから100メートル以内にあるアドレスを見つけるために互いにテーブルを作成します。これにより、SQL Server内に長時間実行される並列タスクが作成され、大きなデカルト結果も生成されます。この動作を自分で再現することにした場合は、これが完了したり、結果が返されることを期待しないでください。クエリが実行されると、タスクの親スレッドは CXPACKETの待機を開始します 待機し、クエリは数分間処理を続行します。ただし、私が興味を持ったのは、CLRランタイムでガベージコレクションが発生した場合、またはGCが呼び出された場合にどうなるかということでした。そのため、ループして手動でGCを強制的に実行する単純なPowerShellスクリプトを使用しました。
注:これは、多くの理由から、プロダクションコードで推奨される方法ではありません!
while (1 -eq 1) {[System.GC]::Collect() }
PowerShellウィンドウが実行されると、ほとんどすぐに CLR_MANUAL_EVENTが表示され始めました。 sys.dm_os_waiting_tasks の並列サブタスクスレッド(以下に示す、exec_context_idがゼロより大きい)で発生する待機 :
この動作をトリガーでき、SQL Serverが必ずしもここで問題であるとは限らず、他のアクティビティの犠牲になっている可能性があることが明らかになり始めたので、問題の根本原因をより深く掘り下げて特定する方法を知りたいと思いました。 。ここで、PerfMonは、サーバー上のすべてのタスクの.NETCLRメモリカウンターグループを追跡するのに役立ちました。
このスクリーンショットは、 sqlservrのコレクションを表示するために縮小されています。 およびpowershell _Global_と比較したアプリケーションとして .NETランタイムによるコレクション。 GC.Collect()を強制する 常に実行するために、 powershell インスタンスはサーバー上のGCコレクションを駆動しています。このPerfMonカウンターグループを使用して、どのアプリケーションが最も多くのコレクションを実行しているかを追跡し、そこから問題のさらなる調査を続けることができます。この場合、PowerShellスクリプトを停止するだけで、 CLR_MANUAL_EVENTが削除されます。 SQL Server内で待機し、クエリは、停止するか、SQLServerによって出力される10億行の結果を返すことができるようになるまで処理を続行します。
結論
CLR_MANUAL_EVENTをアクティブに待機している場合 アプリケーションの速度低下を引き起こしますが、問題がSQLServerの内部にあると自動的に想定しないでください。 SQL Serverはサーバーレベルのガベージコレクションを使用します(少なくとも、2GB未満のRAMを搭載した小規模サーバーがクライアントレベルのガベージコレクションを使用してリソース使用量を削減できるSQL Server 2017 CU4より前のバージョン)。 SQL Serverでこの問題が発生している場合は、PerfMonの.NET CLRメモリカウンターグループを使用して、別のアプリケーションがCLRでガベージコレクションを実行し、結果としてSQLServerの内部でCLRタスクをブロックしていないかどうかを確認してください。