Как 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 неправильно".