Как SqlConnection управляет IsolationLevel?

Эта статья статьи MSDN гласит, что:

Уровень изоляции имеет общий доступ область действия и один раз для подключения с изоляцией SET TRANSACTION LEVEL, он остается в силе пока соединение не будет закрыто или устанавливается другой уровень изоляции. Когда соединение закрывается и возвращается пул, уровень изоляции от последний УРОВЕНЬ ИЗОЛЯЦИИ УСТРОЙСТВА СТАВКИ утверждение сохраняется. последующее соединения, использующие объединенные подключение использует уровень изоляции который действовал в то время, когда соединение объединено.

Класс SqlConnection не имеет члена, который может удерживать уровень изоляции. Итак, как соединение знает, какой уровень изоляции запускать?

Причина, по которой я спрашиваю об этом, связана со следующим сценарием:

  • Я открыл транзакцию, используя TransactionScope в Serializable режиме, скажем, "T1".
  • Открыл соединение для T1.
  • T1 завершен/удален, соединение возвращается в пул соединений.
  • Вызов другого запроса на тот же соединение (после получения пул соединений), и этот запрос выполняется в сериализованном режиме!!!

Проблема:

  • Как работает объединенное соединение знать, какой уровень изоляции был связанные с ним???
  • Как вернуть его обратно уровень транзакции

Разрешение:
Причина, по которой объединенные соединения возвращают сериализуемый уровень изоляции, объясняется следующей причиной:

  • У вас есть один пул соединений (скажем, CP1)
  • CP1 может иметь 50 подключений.
  • Вы выбираете одно соединение C1 из CP1 и выполняете его с помощью Serializable. Теперь это соединение имеет уровень изоляции. Что бы вы ни делали, это не будет reset (если это соединение не будет используется для выполнения кода на другом уровне изоляции).
  • После выполнения запроса C1 (Serializable) возвращается к CP1.
  • Если шаги 1-4 выполняются снова, тогда используемое соединение может быть каким-то другим соединением, чем C1, пусть C2 или C3. Таким образом, это также будет установите уровень изоляции на Serializable.
  • Итак, медленно, Serialzable устанавливается на несколько соединений в CP1.
  • Когда вы выполняете запрос, где не выполняется явная настройка уровня изоляции, соединение, выбранное из CP1, будет определять уровень изоляции. Напр. если такой запрос запрашивает соединение и CP1 использует C1 (Serializable) для выполнения этого запроса, затем этот запрос будет выполняться в режиме Serializable, даже если вы явно не указали установите его.

Надеюсь, что устраняются несколько сомнений.:)

Ответ 1

Уровни изоляции реализованы в базовых СУБД, например SqlServer. Установка уровня изоляции, скорее всего, устанавливает команды SQL, которые устанавливают уровень изоляции для соединения.

СУБД сохраняет уровень изоляции, пока соединение остается открытым. Поскольку соединения помещаются в пул, он остается открытым и сохраняет настройки ранее.

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

Ответ 2

SqlConnection.BeginTransaction принимает аргумент IsolationLevel и таким образом контролируется уровень изоляции соединений SqlClient. Другой вариант - использовать общие System.Transactions и указать уровень изоляции в TransactionOptions.IsolationLevel, переданный TransactionScope конструктор. Как в модели программирования SqlClient, так и в System.Transactions уровень изоляции должен быть явно определен для каждой транзакции. Если не указано, будет использоваться значение по умолчанию (Read Committed for SqlClient, Serializable для System.Transactions).

Объединенные соединения не используются повторно. У них есть скрытые внутренние члены для отслеживания текущего состояния, такого как текущая транзакция, ожидающих результатов и т.д., И среда может очистить соединение, возвращенное в пул. Просто потому, что состояние не отображается в модели программирования, это не значит, что это не так (это относится к любому классу библиотеки, любой конструктор классов может скрыть элемент под internal зонтик).

И, наконец, любое соединение, повторно используемое из пула, которое он вызывает sp_reset_connection, который является серверной процедурой, которая очищает состояние сеанса на стороне сервера.

Ответ 3

Он не возвращает уровень изоляции в исходное значение. В примере с использованием объектов требуется пустая транзакция для уровня reset (хотя это явно не нужно делать (нет .Complete()).

Попытка изменить уровень iso с использованием SP на сервере DB не работает. Выход:

До: ReadCommitted
   Во время: Serializable
   После: Serializable
   После reset с помощью SP Попытка: Serializable
   Во время reset by XACT: ReadCommitted
   После reset с помощью XACT: ReadCommitted

// using Dbg = System.Diagnostics.Debug;
XactIso.iso isoEntity = new XactIso.iso();
using (isoEntity)
{
    Dbg.WriteLine("Before: " + isoEntity.usp_GetXactIsoLevel().SingleOrDefault());

    var xactOpts = new TransactionOptions();
    xactOpts.IsolationLevel = System.Transactions.IsolationLevel.Serializable;

    using (TransactionScope xact = new TransactionScope(TransactionScopeOption.Required, xactOpts))
    {
        Dbg.WriteLine("During: " + isoEntity.usp_GetXactIsoLevel().SingleOrDefault());
        xact.Complete();
    }

    Dbg.WriteLine("After: " + isoEntity.usp_GetXactIsoLevel().SingleOrDefault());

    isoEntity.usp_SetXactIsoLevel("ReadCommitted");

    Dbg.WriteLine("After Reset by SP Attempt: " + isoEntity.usp_GetXactIsoLevel().SingleOrDefault());
    // failed

    var xactOpts2 = new TransactionOptions();
    xactOpts2.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
    using (TransactionScope xact2 = new TransactionScope(TransactionScopeOption.Required, xactOpts2))
        Dbg.WriteLine("During Reset by XACT: " + isoEntity.usp_GetXactIsoLevel().SingleOrDefault());
    // works w/o commit

    Dbg.WriteLine("After Reset by XACT: " + isoEntity.usp_GetXactIsoLevel().SingleOrDefault());
}

где из ссылки

proc [Common].[usp_GetXactIsoLevel]
as
begin          
    select         
        case transaction_isolation_level 
            WHEN 0 THEN 'Unspecified' 
            WHEN 1 THEN 'ReadUncommitted' 
            WHEN 2 THEN 'ReadCommitted' 
            WHEN 3 THEN 'RepeatableRead' 
            WHEN 4 THEN 'Serializable' 
            WHEN 5 THEN 'Snapshot' 
        end as lvl
     from sys.dm_exec_sessions 
    where session_id = @@SPID;
end

и (не работает):

proc [Common].[usp_SetXactIsoLevel]
    @pNewLevel    varchar(30)
as
begin

    if @pNewLevel = 'ReadUncommitted'
        SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
    else if @pNewLevel = 'ReadCommitted'
        SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
    else if @pNewLevel = 'RepeatableRead'
        SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
    else if @pNewLevel = 'Serializable'
        SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
    else if @pNewLevel = 'Snapshot'
        SET TRANSACTION ISOLATION LEVEL SNAPSHOT;
    else
        raiserror('Unrecognized Transaction Isolation Level', 16, 1);         
end        

Ответ 4

В SQL Server 2014 уровень изоляции для объединенного соединения - reset, когда соединение возвращается в пул. См. этот пост форума

"в SQL 2014, для клиентских драйверов с TDS версии 7.3 или выше SQL-сервер будет reset уровень изоляции транзакций по умолчанию (чтение считается) для объединенных подключений. для клиентов с версией TDS ниже 7.3 они будут иметь старую поведение при работе с SQL 2014."

Обновление 2017-04-22

К сожалению, это было позже "незафиксировано" в SQL Server 2014 CU6 и SQL Server 2014 SP1 CU1, поскольку он ввел ошибку:

FIX: Уровень изоляции транзакции reset некорректно, когда соединение SQL Server выпущено в SQL Server 2014

"Предположим, что вы используете класс TransactionScope в исходном коде на стороне клиента SQL Server, и вы явно не открываете соединение SQL Server в транзакции. Когда соединение SQL Server освобождается, уровень изоляции транзакции составляет reset неправильно".