2011-12-13 69 views
2

我有一臺數據庫運行在一臺機器上,用於逐秒更新信息。我的所有客戶提供的數據都返回到一個源,然後將數據發佈到數據庫。所有這些都很好,我遇到的問題是我的服務代理和聆聽變化。SQL Server 2008 R2快速服務代理正在使用所有空閒內存

我正在使用SqlDependency對象來註冊特定查詢的偵聽器。我按預期得到事件,但服務器最終變得無法使用。我能夠確定Service Broker正在使用超過900MB的內存(推動我超過1GB的限制)。我有這樣的印象:我的事件一直停留在記憶中,沒有清理出去。每次我收到一個事件時,我都會清除該事件監聽器並註冊一個新事件。有沒有更好的方法來清理數據庫中的事件?

另外,我已經讀過,你必須在每次註冊一個新事件時都要叫Stop and Start。根據我的經驗,再次呼叫停止總是永遠掛起。另外,如果停止清除所有事件,我有多個聽衆,並且當我收到其他人時,我不想停止其他人。

這裏是我使用的註冊和響應事件的代碼:

using (SqlConnection cn = new SqlConnection(Properties.Settings.Default.DatabseEventConnectionString)) 
{ 
    using (SqlCommand cmd = cn.CreateCommand()) 
    { 
     cmd.CommandType = CommandType.Text; 
     cmd.CommandText = "SELECT Field1, Field2, Field3, Field4 FROM dbo.Table"; 
     cmd.Notification = null; 

     SqlDependency dep = new SqlDependency(cmd); 
     dep.OnChange += new OnChangeEventHandler(dependency_OnChange); 

     cn.Open(); 

     using (SqlDataReader reader = cmd.ExecuteReader()) 
     { 
      // Handle read here; 
     } 
    } 
} 

void dependency_OnChange(object sender, SqlNotificationEventArgs e) 
{ 
    // If InvokeRequired returns True, the code 
    // is executing on a worker thread. 
    if (Dispatcher.CheckAccess()) 
    { 
     SqlDependency dep = sender as SqlDependency; 
     dep.OnChange -= new OnChangeEventHandler(dependency_OnChange); 
     RegisterTableListener(); 
    } 
    else 
    { 

     // Create a delegate to perform the thread switch. 
     OnChangeEventHandler tempDelegate = 
      new OnChangeEventHandler(dependency_OnChange); 

     object[] args = { sender, e }; 

     // Marshal the data from the worker thread 
     // to the UI thread. 
     Dispatcher.Invoke(tempDelegate, args); 
    } 
} 

任何想法,爲什麼內存是永攀?

+0

正在運行測試和內存只是永遠不會下降。即使在啓動一個新實例後關閉了我的應用程序並調用了停止之後,內存仍會繼續攀升並永遠不會釋放。 – JeremyK 2011-12-13 20:44:48

+0

我們與服務代理人有類似的問題,我相信它的應用程序並沒有結束它創建的對話。 – user957902 2011-12-13 22:16:02

回答

1

存在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

我不是專家,但...

你試過:

using (SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection)) 
    { 
     // Handle read here; 
    } 

,並可能改變 dep.OnChange - =新OnChangeEventHandler(dependency_OnChange); 至 dep.OnChange - = dependency_OnChange;

相關問題