SQL Server 2008 R2 Express Service Broker использует всю свободную память - PullRequest
2 голосов
/ 13 декабря 2011

У меня есть база данных, работающая на машине, которая служит для обновления информации от секунды к секунде. Все мои клиенты предоставили данные, все они возвращаются в один источник, который, в свою очередь, публикует данные в базе данных. Все это хорошо, проблема, с которой я сталкиваюсь, связана с моим сервис-брокером и прослушиванием изменений.

Я использую объект SqlDependency для регистрации слушателей для конкретных запросов. Я получаю события, как ожидалось, однако сервер в конечном итоге становится непригодным для использования. Я смог определить, что компонент Service Broker использует более 900 МБ памяти (что приводит к превышению лимита в 1 ГБ для экспресс-доставки). У меня сложилось впечатление, что мои события остаются в памяти и не очищаются. Каждый раз, когда я получаю событие, я очищаю этого слушателя события и регистрирую нового. Есть ли более правильный способ удалить это событие из базы данных?

Кроме того, я прочитал все, что вы должны вызывать Stop и Start каждый раз, когда вы регистрируете новое событие. По моему опыту, вызов Stop во второй раз всегда зависает навсегда. Кроме того, если остановка удаляет все события, у меня есть несколько слушателей, и я не хочу останавливать другие, когда получаю одно.

Вот код, который я использую для регистрации и ответа на события:

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

Есть идеи, почему память вечно взбирается?

Ответы [ 2 ]

1 голос
/ 29 января 2015

Существует специфическое поведение класса Microsoft SqlDependency. Несмотря на то, что вы вызываете метод SqlDependency.Stop (), освобождаете SqlCommand и SqlConnection - он по-прежнему сохраняет группы базы данных (sys.conversation_groups) и конечные точки диалога (sys.conversation_endpoints) в базе данных. Похоже, что SQL Server загружает каждую конечную точку диалога и использует всю разрешенную память. Здесь тесты, которые доказывают это. Итак, чтобы очистить все неиспользуемые конечные точки диалога и освободить всю занятую память, вам нужно запустить этот код 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 голос
/ 14 декабря 2011

Я не эксперт, но ...

Вы пробовали:

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

и возможно изменение dep.OnChange - = new OnChangeEventHandler (dependency_OnChange); в dep.OnChange - = dependency_OnChange;

...