2011-12-14 2 views
9

Comment puis-je nettoyer le SQL Server pour se débarrasser des objets SqlDependency expirés? Après avoir reçu l'événement de l'objet SqlDepedency, je dois en créer un nouveau avant de pouvoir obtenir un nouvel événement. Toutefois, l'utilisation de la mémoire du processus SQL Server grimpe jusqu'à ce qu'il soit épuisé de la mémoire autorisée (SQL Server Express). Comment puis-je me débarrasser des anciennes requêtes?Comment nettoyer SqlDependency à partir de la mémoire SQL Server?

code:

// 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

Comment créez-vous les objets 'SqlDependency'? S'il vous plaît poster votre code. Les disposez-vous correctement? – Oded

+0

Je mets à jour mon commentaire avec du code quand je serai au travail demain. Sudo: SqlDependency dep = nouvelle SqlDependency (cmd); dep.OnChange + = amusant; SqlDependency n'implémente pas IDisposable – JeremyK

+0

J'ai mis à jour avec du code. Même lorsque je lance une seule instance d'un SqlDepdency et que j'appelle Stop et Start à chaque fois, la mémoire grimpe. Je n'ai aucune idée de ce qui se passe. – JeremyK

Répondre

12

Il y a un comportement spécifique de la classe Microsoft SqlDependency. Même si vous appelez la méthode SqlDependency.Stop(), libérez SqlCommand et SqlConnection - il conserve les groupes de conversation (sys.conversation_groups) et les points de terminaison de conversation (sys.conversation_endpoints) dans la base de données. Il semble que SQL Server charge chaque point de terminaison de conversation et utilise toute la mémoire autorisée. Here tests qui le prouvent. Donc, pour nettoyer tous les points d'extrémité de conversation non utilisés et libérer toute la mémoire occupée vous devez commencer ce code SQL pour la base de données:

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; 

Aussi, SqlDependency ne vous donne pas la possibilité de recevoir toutes les modifications du tableau. Ainsi, vous ne recevez pas de notification sur les modifications lors de la réabonnement SqlDependency.

Pour éviter tous ces problèmes, j'avais utilisé une autre réalisation Open Source de la classe SqlDependency - SqlDependencyEx. Il utilise un déclencheur de base de données et une notification Service Broker native pour recevoir des événements sur les modifications de la table. Ceci est un exemple d'utilisation:

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); 

Espérons que cela aide.

+1

quelle est la signification de cette ligne WHERE CEP.state = 'DI' ou CEP.state = 'CD'. qu'est-ce que tu essayes de faire. s'il vous plaît, aidez-moi à comprendre. merci – Mou

+0

@Mou 'DI' signifie" DisconnectedInbound ",' CD' signifie "Fermé". Les points de terminaison Conversations avec ces marques n'ont pas de durée de vie définie par 'SqlDependency'. Cela signifie qu'ils seront dans une base de données jusqu'à ce que vous les nettoyiez avec force. Selon l'article http://rusanu.com/2014/03/31/how-to-prevent-conversation-endpoint-leaks/ (à la fin) c'est une bonne façon de nettoyer les anciens points de terminaison de la conversation. – dyatchenko

+0

cela signifie-t-il que si j'utilise ce sql 'WHERE CEP.state = 'DI' ou CEP.state = 'CD'' alors la vieille conversation va supprimer? – Mou

0

Je suis face à exactement le même problème. Je crée un composant d'accès aux données qui met en cache certaines requêtes à partir d'une base de données SQL Server 2005. Le cache est invalidé à l'aide de cette nouvelle approche brillante, bien moins récente, SqlDependency. Étant donné que ce composant sera utilisé dans ASP.NET ainsi que dans les applications Service Forms et Windows, je recherche un moyen commun (interne) d'appeler SqlDependency.Stop(). L'utilisation d'un finaliseur était aussi ma première idée, et cela n'a pas fonctionné. Mon deuxième essai utilisait un gestionnaire d'événements pour AppDomain.DomainUnload.

Après tout, cela semble fonctionner ... Mais le serveur web intégré dans VS 2005 va se bloquer pendant 4-5 minutes avec 100% CPU tout en exergeant SqlDependy.Stop(). En fait, je ne me souviens d'aucun autre processus bloquant ma machine (ordinateur portable Pentium M) reproductible si mal que je pourrais difficilement faire apparaître le Gestionnaire des tâches ... Je ne m'attendais pas à ce que cela soit possible depuis l'espace utilisateur et même le code managé (SQL Server est en cours d'exécution sur une autre boîte.) Pendant ce temps, même l'Analyseur de performances refuse d'enregistrer quoi que ce soit, donc je ne peux pas dire s'il y a beaucoup de poignées Windows ou d'exceptions .NET

L'appeler à partir de l'événement Application_End fonctionne correctement (et ne prend que quelques millisecondes), mais il est spécifique à ASP.NET.

Toutes les idées

+0

J'ai fini par renoncer à utiliser le service broker. Je n'ai pas été capable de comprendre ce qui causait la fuite. – JeremyK

Questions connexes