2011-12-14 15 views
9

期限切れのSqlDependencyオブジェクトを取り除くためにSQL Serverをクリーンアップするにはどうすればよいですか? SqlDepedencyオブジェクトからイベントを受け取った後、新しいイベントを取得する前に新しいイベントを作成する必要があります。ただし、SQL Serverプロセスのメモリ使用量は、許可されたメモリ(SQL Server Express)を使い果たすまで増加します。古いクエリを取り除くにはどうすればよいですか?SQL ServerメモリからSqlDependencyを消去するにはどうすればよいですか?

コード:

// Func: RegisterTableListener 
using (SqlConnection cn = new SqlConnection(Properties.Settings.Default.DatabseEventConnectionString)) 
{ 
if (cmd == null) 
{ 
    cmd = cn.CreateCommand(); 

    cmd.CommandType = CommandType.Text; 
    cmd.CommandText = "SELECT HostName, LastStatus, LastDetails, xml FROM dbo.[SystemTable]"; 
} 

lock (cmd) 
{ 
    cmd.Connection = cn; 
    cn.Open(); 
    cmd.Notification = null; 

    // creates a new dependency for the SqlCommand 
    if (dep == null) 
     dep = new SqlDependency(cmd); 
    // creates an event handler for the notification of data 
    //  changes in the database. 
    dep.OnChange += new OnChangeEventHandler(dependency_OnChange); 


    using (SqlDataReader reader = cmd.ExecuteReader()) 
    { 
    // code here to read 
    } 
} 
} 

// Func dependency_OnChange 
//SqlDependency dep = sender as SqlDependency; 
dep.OnChange -= dependency_OnChange; 
RegisterTableListener(); 
+0

どのように 'SqlDependency'オブジェクトを作成していますか?あなたのコードを投稿してください。あなたはそれらを適切に処分していますか? – Oded

+0

明日明日に働くとコードで私のコメントを更新します。 Sudo: SqlDependency dep = new SqlDependency(cmd); OnChange + =楽しい。 SqlDependencyはIDisposableを実装していません – JeremyK

+0

コードで更新しました。 SqlDepdencyのインスタンスを1つだけ実行し、StopとStartを呼び出すたびにメモリが上昇します。私は何が起こっているのかは断りません。 – JeremyK

答えて

12

Microsoft SqlDependencyクラスの特定の動作があります。 SqlDependency.Stop()メソッドを呼び出してもSqlCommandとSqlConnectionを解放しても、会話グループ(sys.conversation_groups)と会話エンドポイント(sys.conversation_endpoints)がデータベースに保持されます。 SQL Serverはすべての会話エンドポイントを読み込み、すべての許可されたメモリを使用するようです。それを証明するテストがHereです。だから、すべての未使用の会話のエンドポイントをきれいにし、あなたがあなたのデータベースに対してこのSQLコードを開始する必要が占有されているすべてのメモリ解放する:

また
DECLARE @ConvHandle uniqueidentifier 
DECLARE Conv CURSOR FOR 
SELECT CEP.conversation_handle FROM sys.conversation_endpoints CEP 
WHERE CEP.state = 'DI' or CEP.state = 'CD' 
OPEN Conv; 
FETCH NEXT FROM Conv INTO @ConvHandle; 
WHILE (@@FETCH_STATUS = 0) BEGIN 
    END CONVERSATION @ConvHandle WITH CLEANUP; 
    FETCH NEXT FROM Conv INTO @ConvHandle; 
END 
CLOSE Conv; 
DEALLOCATE Conv; 

を、SqlDependencyはあなたのテーブルのすべての変更を受信する機会を与えるものではありません。したがって、SqlDependencyの再サブスクリプション中に変更に関する通知を受け取ることはありません。

これらの問題をすべて回避するために、私はSqlDependencyクラスの別のオープンソース化を使用しました(SqlDependencyEx)。データベースのトリガーとネイティブService Broker通知を使用して、テーブルの変更に関するイベントを受け取ります。これは使用例です。

int changesReceived = 0; 
using (SqlDependencyEx sqlDependency = new SqlDependencyEx(
      TEST_CONNECTION_STRING, TEST_DATABASE_NAME, TEST_TABLE_NAME)) 
{ 
    sqlDependency.TableChanged += (o, e) => changesReceived++; 
    sqlDependency.Start(); 

    // Make table changes. 
    MakeTableInsertDeleteChanges(changesCount); 

    // Wait a little bit to receive all changes. 
    Thread.Sleep(1000); 
} 

Assert.AreEqual(changesCount, changesReceived); 

希望すると便利です。

+1

WHERE CEP.state = 'DI'またはCEP.state = 'CD'という行の意味は何ですか?あなたは何をしようとしていますか?私に理解を助けてください。ありがとう – Mou

+0

@Mou「DI」は「DisconnectedInbound」を意味し、「CD」は「Closed」を意味します。このマークが付いた会話のエンドポイントは、 'SqlDependency'によって設定された存続時間を持っていません。それはあなたが強くそれらをきれいにするまで彼らがデータベースにあることを意味します。記事http://rusanu.com/2014/03/31/how-to-prevent-conversation-endpoint-leaks/(最後に)によれば、これは古い会話のエンドポイントをクリーンアップする正しい方法です。 – dyatchenko

+0

私はこのsql 'WHERE CEP.state = 'DI'またはCEP.state = 'CD' 'を使用して古い会話は削除されますか? – Mou

0

私はまったく同じ問題に直面しています。私はSQL Server 2005データベースからいくつかのクエリをキャッシュしているデータアクセスコンポーネントを作成しています。この光沢のある新機能を使用してキャッシュが無効になりました.SoMDependencyのアプローチではなくなりました。

このコンポーネントは、ASP.NETとFormsおよびWindowsサービスアプリケーションで使用されるため、(内部的に)SqlDependency.Stop()を呼び出す一般的な方法を探しています。

ファイナライザを使用することも私の最初のアイデアでしたが、これはうまくいきませんでした。 2回目はAppDomain.DomainUnloadのイベントハンドラを使用していました。

結局のところ、これはうまくいくようです...しかし、VS 2005のビルトインWebサーバーは、SqlDependy.Stop()を実行しながら100%CPUで4-5分間停止します。実際、私のマシン(Pentium Mラップトップ)をブロックするプロセスを思い出すことができないので、タスクマネージャを起動することはほとんどできませんでした。私はこれがユーザー空間と管理されたコードから可能であるとは思わなかった(SQL Serverは別のボックスで実行されています)。この間、パフォーマンスモニタでさえもログに記録を拒否するため、多くのWindowsハンドルや.NET例外があるかどうかは言えません。

Application_Endイベントから呼び出すとうまく動作します(ただし、数ミリ秒しかかかりません)。これはASP.NET固有のものです。

任意のアイデア

+0

私はサービスブローカーの使用を断念しました。私はリークの原因を突き止めることができませんでした。 – JeremyK