2016-05-26 6 views
2

Наша база данных SQL Server 2014 установлена ​​в READ_COMMITTED_SNAPSHOT.Распределенная транзакция с MSMQ и SQL Server, но иногда получение грязных сообщений

Мы используем MSMQ и распределенные транзакции (мы используем MassTransit 2.10)

В одной части нашей системы мы читаем сообщение из очереди, сделать обновление базы данных, а затем опубликовать новое сообщение в очередь (все под одна транзакция).

Мы обнаружили, что обновление не выполняется при обработке следующего сообщения (оно читает из той же таблицы, что и первая часть), хотя я ожидаю, что это сообщение будет находиться только в очереди на в то же время обновляется база данных. Когда мы снова запрашиваем таблицу, обновленные данные там, как и ожидалось. Это происходит только тогда, когда мы имеем высокую нагрузку и очень редко.

Упрощенная версия нашего кода

// code that processes message 1 
using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew, new TransactionOptions() { Timeout = TimeSpan.FromMinutes(30), IsolationLevel = IsolationLevel.ReadCommitted }) 
{ 
    MethodThatUpdatesTableX(); 
    MethodThatCreatesMessage2(); 
    scope.Complete(); 
}  

// message picked up from MSMQ and then (this runs in different thread): 
// code that process message 2 
using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew, new TransactionOptions() { Timeout = TimeSpan.FromMinutes(30), IsolationLevel = IsolationLevel.ReadCommitted }) 
{ 
    MethodThatReadsFromTableX(); // so here it seems that changes made in MethodThatUpdatesTableX is sometimes (though rarely) not read 
    // other stuff 
}  

Так вот мое понимание:

Когда область расположена изменения в таблицу X привержен, а также сообщения, опубликованные в очередь

Когда MethodThatReadsFromTableX() читает из таблицы XI, ожидается, что изменения будут там (сеанс не должен быть создан до того, как первый будет завершен, потому что он не сможет получить сообщение из очереди)

Является ли мое ожидание правильным? В чем проблема?

ответ

1

Я дам вам действительно очень короткий ответ.

Использовать Serializable как ваш уровень изоляции. Это единственный способ гарантировать, что читатели будут заблокированы писателями.

Остальное ...

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

Вам также необходимо убедиться, что ваши читатели, которые могут получать грязные чтения, также читают, используя сериализуемое - в противном случае у вас будет случай, когда публикуемое сообщение потребляется до того, как транзакция базы данных будет совершена. Я видел это в системах с большими объемами, и это происходит (конечно, с RabbitMQ, а не с MSMQ - RabbitMQ отправляет гораздо быстрее, чем MSMQ).

1

Ниже приводятся мои мысли об этой проблеме.

В распределенной транзакции обычно используется двухфазная фиксация. На первом этапе координатор трансаситонов просит всех подготовиться к совершению. Если все согласятся, то во втором этапе координатор обяжет всех совершать изменения. Обратите внимание, что когда узел совершил изменения на этом этапе - он больше не откатится. Также обратите внимание, что может быть неизбежный разрыв между участником A (например, сервером Sql), и участник B (MSMQ) фактически совершает свои изменения. Когда MSMQ уже совершил изменения, возможно, SQL Server еще не был (хотя уже был назначен).

Таким образом, когда MSMQ совершает транзакцию, ничто не мешает созданию готового сообщения на ваш процессор, который, как вы сказали, работает в другом потоке. Поэтому даже до того, как вы покинете первый блок «scope» - ваш процессор может начать обработку полученного сообщения, а сервер SQL может еще не внесены изменения. Распределенная транзакция не может предотвратить такое поведение, поскольку она не может заставить каждого участника закончить выполнение своих изменений точно в одно и то же время.

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