TransactionScope автоматически переходит в MSDTC на некоторых машинах?


284

В нашем проекте мы используем TransactionScope, чтобы гарантировать, что наш уровень доступа к данным выполняет свои действия в транзакции. Мы стремимся не требовать, чтобы служба MSDTC была включена на компьютерах наших конечных пользователей.

Проблема в том, что на половине машин наших разработчиков мы можем работать с отключенным MSDTC. Другая половина должна иметь его включенным, или они получают сообщение об ошибке «MSDTC на [SERVER] недоступен» .

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

Я надеялся на лучший ответ Trace, почему транзакция переходит в DTC, но, к сожалению, это не так.

Вот пример кода, который вызовет проблему: на машинах, которые пытаются эскалировать, он пытается эскалировать при втором соединении. Open () (и да, в данный момент нет других открытых соединений).

using (TransactionScope transactionScope = new TransactionScope() {
   using (SqlConnection connection = new SqlConnection(_ConStr)) {
      using (SqlCommand command = connection.CreateCommand()) {
         // prep the command
         connection.Open();
         using (SqlDataReader reader = command.ExecuteReader()) {
            // use the reader
            connection.Close();
         }
      }
   }

   // Do other stuff here that may or may not involve enlisting 
   // in the ambient transaction

   using (SqlConnection connection = new SqlConnection(_ConStr)) {
      using (SqlCommand command = connection.CreateCommand()) {
         // prep the command
         connection.Open();  // Throws "MSDTC on [SERVER] is unavailable" on some...

         // gets here on only half of the developer machines.
      }
      connection.Close();
   }

   transactionScope.Complete();
}

Мы действительно закопались и попытались выяснить это. Вот некоторая информация о машинах, на которых он работает:

  • Dev 1: Windows 7 x64 SQL2008
  • Dev 2: Windows 7 x86 SQL2008
  • Dev 3: Windows 7 x64 SQL2005 SQL2008

Разработчики, на которых это не работает:

  • Dev 4: Windows 7 x64, SQL2008 SQL2005
  • Dev 5: Windows Vista x86, SQL2005
  • Dev 6: Windows XP X86, SQL2005
  • Мой домашний ПК: Windows Vista Home Premium, x86, SQL2005

Я должен добавить, что все машины, чтобы выследить проблему, были полностью исправлены всем, что доступно из Центра обновления Microsoft.

Обновление 1:

На этой странице эскалации транзакций MSDN указано, что при следующих условиях транзакция переходит в DTC:

  1. По крайней мере один долговременный ресурс, который не поддерживает однофазные уведомления, зачислен в транзакцию.
  2. По крайней мере два долговременных ресурса, которые поддерживают однофазные уведомления, зачисляются в транзакцию. Например, использование одного соединения с не приводит к продвижению транзакции. Однако всякий раз, когда вы открываете второе соединение с базой данных, в результате чего база данных подключается, инфраструктура System.Transactions обнаруживает, что это второй долговременный ресурс в транзакции, и преобразует его в транзакцию MSDTC.
  3. Вызывается запрос на «преобразование» транзакции в другой домен приложения или в другой процесс. Например, сериализация объекта транзакции через границу домена приложения. Объект транзакции является маршалированным по значению, что означает, что любая попытка передать его через границу домена приложения (даже в том же процессе) приводит к сериализации объекта транзакции. Вы можете передать объекты транзакции, сделав вызов удаленного метода, который принимает транзакцию в качестве параметра, или вы можете попытаться получить доступ к удаленному компоненту, обслуживаемому транзакцией. Это сериализует объект транзакции и приводит к эскалации, как при сериализации транзакции в домене приложения. Он распространяется, и локальный менеджер транзакций больше не подходит.

Мы не испытываем № 3. # 2 не происходит, потому что есть только одно соединение за раз, и это также к одному «долговременному ресурсу». Есть ли способ, которым # 1 могло случиться? Некоторая конфигурация SQL2005 / 8, которая заставляет его не поддерживать однофазные уведомления?

Обновление 2:

Лично повторно исследовали все версии SQL Server - «Dev 3» на самом деле имеет SQL2008, а «Dev 4» на самом деле SQL2005. Это научит меня никогда больше не доверять моим коллегам. ;) Из-за этого изменения данных, я почти уверен, что мы нашли нашу проблему. Наши разработчики SQL2008 не сталкивались с этой проблемой, потому что SQL2008 содержал огромное количество замечательных включений, которых нет в SQL2005.

Это также говорит мне, что, поскольку мы собираемся поддерживать SQL2005, мы не можем использовать TransactionScope, как мы это делали, и если мы хотим использовать TransactionScope, нам нужно будет передавать один объект SqlConnection вокруг ... что кажется проблематичным в ситуациях, когда нельзя легко обойти SqlConnection ... он просто пахнет экземпляром global-SqlConnection. Pew!

Обновление 3

Просто чтобы уточнить здесь в вопросе:

SQL2008:

  • Позволяет несколько подключений в одном TransactionScope (как показано в приведенном выше примере кода).
  • Предупреждение # 1: Если эти несколько SqlConnections вложены, то есть два или более SqlConnections открываются одновременно, TransactionScope немедленно переходит в DTC.
  • Предупреждение № 2: Если дополнительный SqlConnection открыт для другого «долговременного ресурса» (т. Е. Другого SQL Server), он немедленно переходит в DTC

SQL2005:

  • Не разрешает множественные соединения в пределах одного TransactionScope, точка. Он будет увеличиваться, если / если открыт второй SqlConnection.

Обновление 4

В интересах сделать этот вопрос еще больше беспорядка полезно, и просто ради большей ясности, вот как вы можете получить SQL2005 нагнетать ДТК с одного SqlConnection :

using (TransactionScope transactionScope = new TransactionScope()) {
   using (SqlConnection connection = new SqlConnection(connectionString)) {
      connection.Open();
      connection.Close();
      connection.Open(); // escalates to DTC
   }
}

Это просто кажется мне сломанным, но я думаю, я могу понять, SqlConnection.Open()захватывает ли каждый вызов из пула соединений.

"Почему это могло случиться, хотя?" Что ж, если вы используете SqlTableAdapter для этого соединения до его открытия, SqlTableAdapter откроет и закроет соединение, фактически завершив транзакцию за вас, потому что теперь вы не можете открыть ее заново.

Таким образом, в основном, для успешного использования TransactionScope с SQL2005 необходимо иметь какой-то глобальный объект соединения, который остается открытым с момента создания первого TransactionScope до тех пор, пока он больше не понадобится. Помимо запаха кода глобального объекта соединения, открытие первого соединения и его закрытие последним противоречит логике открытия соединения как можно позже и закрытия его как можно скорее.


Можете ли вы расширить «Делать другие вещи здесь, которые могут или не могут участвовать в окружающей транзакции». Конечно, что там сильно влияет на поведение кода?
RichardOD

2
«# 2 не происходит, потому что существует только одно соединение за раз» - # 2 не говорит о том, что второе соединение должно быть открыто одновременно, просто что оно должно быть зачислено в той же транзакции.
Джо

3
Большое спасибо за сообщение об обновлении 4, показывающее, как может произойти эскалация всего с одним SqlConnection. Это именно то, с чем я столкнулся, несмотря на тщательную проверку того, что используется только один SqlConnection. Приятно знать, что сумасшедший компьютер, а не я. :-)
Оран Деннисон

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

1
Вложенные соединения в одной и той же области транзакции будут преобразованы в распределенную транзакцию. Начиная с SQL Server 2008 и выше, несколько (не вложенных) подключений в одной и той же области транзакции не будут преобразованы в распределенную транзакцию.
PreguntonCojoneroCabrón

Ответы:


71

SQL Server 2008 может использовать несколько SQLConnections в одном TransactionScopeбез эскалации, при условии, что соединения не открываются одновременно, что приведет к нескольким «физическим» TCP-соединениям и, следовательно, потребует эскалации.

Я вижу, что у некоторых из ваших разработчиков есть SQL Server 2005, а у других - SQL Server 2008. Вы уверены, что правильно определили, какие из них обостряются, а какие нет?

Наиболее очевидным объяснением будет то, что разработчики с SQL Server 2008 - это те, которые не обостряются.


Да, детали верны, и кто-нибудь на самом деле смотрит на код? В области транзакции есть два соединения, однако только одно соединение создается и открывается в один момент времени. Кроме того, нет, DTC не работает на машинах, которые работают.
Yoopergeek

1
«однако, в любой момент времени может быть установлено и открыто только одно соединение» - почему это актуально? В SQL2005, если вы открываете более одного соединения в рамках транзакции, вы будете увеличивать, остаются ли они открытыми одновременно. Что логично, если вы думаете об этом.
Джо

Теперь у вас с hwiechers есть второе предположение, и я очень хочу приступить к работе в понедельник, более внимательно осмотреть их отдельные машины и убедиться, что версии SQL Server соответствуют ранее сообщенным.
Yoopergeek

19
Вы и hwiechers правы. У меня яйцо по всему лицу. Спасибо, что ударили меня палкой-подсказкой. :) Потому что ты был первым, ты получил ответ. Хотелось бы добавить одно пояснение: SQL2008 позволяет открывать несколько соединений, но не одновременно. В любой момент времени может быть открыто только одно соединение, иначе TransactionScope переходит в DTC.
Yoopergeek

@Yoopergeek Я мог убедиться, что твое "не в то же время" важно, и соответственно отредактировал ответ @Joe. Мониторинг соединений TCP во время тестирования показал, что старое соединение TCP будет использоваться повторно, если соединения не используются в одно и то же время, и, следовательно, TransactionScopeможет обойтись одним соединением COMMITна стороне сервера, что сделает эскалацию излишней.
Евгений Бересовский

58

Результат моего исследования по теме:

введите описание изображения здесь

См. Избежание нежелательной эскалации в распределенных транзакциях.

Я все еще исследую поведение эскалации Oracle: увеличиваются ли транзакции, охватывающие несколько соединений с одной и той же БД, в DTC?


1
Спасибо, что поделились своими исследованиями. Это действительно помогло. Еще один быстрый запрос. В чем разница между TransactionScope () и sqlConnection.BeginTransaction ()?
Baig

В соответствии с этим запросом функции ODAC 12C теперь должен вести себя как SQL 2008, не продвигаясь к распределенным при использовании последовательных подключений к одному и тому же источнику данных.
Фредерик,

31

Этот код будет вызывать эскалацию при подключении к 2005 году.

Проверьте документацию на MSDN - http://msdn.microsoft.com/en-us/library/ms172070.aspx

Рекламные транзакции в SQL Server 2008

В версии 2.0 .NET Framework и SQL Server 2005 открытие второго соединения внутри TransactionScope автоматически переводит транзакцию в полную распределенную транзакцию, даже если оба соединения используют одинаковые строки подключения. В этом случае распределенная транзакция добавляет ненужные издержки, которые снижают производительность.

Начиная с SQL Server 2008 и версии 3.5 .NET Framework, локальные транзакции больше не преобразуются в распределенные транзакции, если в транзакции открывается другое соединение после закрытия предыдущей транзакции. Это не требует никаких изменений в вашем коде, если вы уже используете пул соединений и участвуете в транзакциях.

Я не могу объяснить, почему Dev 3: Windows 7 x64, SQL2005 успешно и Dev 4: Windows 7 x64 терпит неудачу. Вы уверены, что это не наоборот?


10

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

ответил 4 августа 2010 в 17:42 Эдуардо

  1. Установите Enlist = false в строке подключения, чтобы избежать автоматического включения в транзакцию.

  2. Вручную подключить соединение в качестве участников в области транзакции. [ оригинальная статья устарела] или сделайте это: Как предотвратить автоматическое продвижение MSDTC [archive.is]


msdn.microsoft.com/en-us/library/ms172153%28v=VS.80%29.aspx не найден, документация для Visual Studio 2005,
вышедшая из употребления

2

Я не слишком уверен, если вложенное соединение является проблемой. Я вызываю локальный экземпляр сервера SQL, и он не генерирует код неисправности ??

    public void DoWork2()
    {
        using (TransactionScope ts2 = new TransactionScope())
        {
            using (SqlConnection conn1 = new SqlConnection("Data Source=Iftikhar-PC;Initial Catalog=LogDB;Integrated Security=SSPI;"))
            {
                SqlCommand cmd = new SqlCommand("Insert into Log values(newid(),'" + "Dowork2()" + "','Info',getDate())");
                cmd.Connection = conn1;
                cmd.Connection.Open();
                cmd.ExecuteNonQuery();

                using (SqlConnection conn2 = new SqlConnection("Data Source=Iftikhar-PC;Initial Catalog=LogDB;Integrated Security=SSPI;Connection Timeout=100"))
                {
                    cmd = new SqlCommand("Insert into Log values(newid(),'" + "Dowork2()" + "','Info',getDate())");
                    cmd.Connection = conn2;
                    cmd.Connection.Open();
                    cmd.ExecuteNonQuery();
                }
            }

            ts2.Complete();
        }
    }

Какой выпуск SQL Server вы используете? Интересно, нужно ли обновить ответ @Peter Meinl, чтобы отразить какие-либо изменения, сделанные в 2008R2 и / или Denali.
Yoopergeek

Я использую SQL Server 2008 R2.
Ифтихар Али

Интересно, лучше ли ведет себя 2008 R2? Ответ @hwiechers также заставляет меня задуматься о том, предотвращает ли эскалация версия платформы, против которой вы работаете. Наконец, мне интересно, имеет ли это значение локальный экземпляр R2. Хотелось бы, чтобы у меня было время / ресурсы, чтобы изучить, как это изменилось с выпуском 2008 R2 и SQL Server 2012.
Yoopergeek

Не уверен, что проблема заключается во вложенном соединении? лол ... хорошо, блин, удаляй это тогда !, с какой стати люди используют гнезда, когда это не является абсолютно необходимым, я никогда не узнаю.
Пол Захра

1

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

«Проблема в том, что на половине машин наших разработчиков мы можем работать с отключенным MSDTC». Вы уверены, что он отключен;)


0

Убедитесь, что ваша connectionString не устанавливает для пула значение false. Это приведет к новому соединению для каждого нового SqlConnection в TransactionScope и преобразует его в DTC.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.