Уровень транзакции и уровень изоляции
у нас есть проблема использования TransactionScope. TransactionScope обеспечивает нам очень хорошую гибкость при использовании транзакций через наш уровень доступа к данным. Таким образом, мы можем использовать транзакции неявные или явные. Есть некоторые повышения производительности снова ADO.NET транзакций, но в это время это не проблема. Однако у нас есть проблема с блокировкой. В приведенном ниже примере кода, хотя уровень изоляции установлен в ReadCommitted, сделать предложение Select SQL из другого клиента в таблице testTable невозможно, пока основная транзакция (в основном методе) не будет зафиксирована, поскольку на всей таблице есть блокировка. Мы также пытались использовать только одно соединение для всех методов, но такое же поведение. Наша СУБД - это SQL Server 2008. Есть ли что-то, чего мы не поняли?
Отношения
Антон Кальчик
Смотрите этот пример кода:
class Program
{
public class DAL
{
private const string _connectionString = @"Data Source=localhost\fsdf;Initial Catalog=fasdfsa;Integrated Security=SSPI;";
private const string inserttStr = @"INSERT INTO dbo.testTable (test) VALUES(@test);";
/// <summary>
/// Execute command on DBMS.
/// </summary>
/// <param name="command">Command to execute.</param>
private void ExecuteNonQuery(IDbCommand command)
{
if (command == null)
throw new ArgumentNullException("Parameter 'command' can't be null!");
using (IDbConnection connection = new SqlConnection(_connectionString))
{
command.Connection = connection;
connection.Open();
command.ExecuteNonQuery();
}
}
public void FirstMethod()
{
IDbCommand command = new SqlCommand(inserttStr);
command.Parameters.Add(new SqlParameter("@test", "Hello1"));
using (TransactionScope sc = new TransactionScope(TransactionScopeOption.Required))
{
ExecuteNonQuery(command);
sc.Complete();
}
}
public void SecondMethod()
{
IDbCommand command = new SqlCommand(inserttStr);
command.Parameters.Add(new SqlParameter("@test", "Hello2"));
using (TransactionScope sc = new TransactionScope(TransactionScopeOption.Required))
{
ExecuteNonQuery(command);
sc.Complete();
}
}
}
static void Main(string[] args)
{
DAL dal = new DAL();
TransactionOptions tso = new TransactionOptions();
tso.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
using (TransactionScope sc = new TransactionScope(TransactionScopeOption.Required,tso))
{
dal.FirstMethod();
dal.SecondMethod();
sc.Complete();
}
}
}
Ответы
Ответ 1
Я не думаю, что ваша проблема имеет какое-либо отношение к концепции .NET TransactionScope. Скорее, похоже, что вы описываете ожидаемое поведение транзакций SQL Server. Кроме того, изменение уровня изоляции влияет только на "чтение данных", а не "запись данных". Из SQL Server BOL:
"Выбор уровня изоляции транзакций не влияет на блокировки, полученные для защиты изменений данных. Транзакция всегда получает эксклюзивную блокировку любых данных, которые она модифицирует, и удерживает эту блокировку до завершения транзакции независимо от уровня изоляции, установленного для этого транзакция. Для операций чтения уровни изоляции транзакций в первую очередь определяют уровень защиты от эффектов изменений, сделанных другими транзакциями".
Это означает, что вы можете предотвратить поведение блокировки, изменив уровень изоляции для клиента, выдающего инструкцию (SELECT
). Уровень изоляции READ COMMITED
(по умолчанию) не будет препятствовать блокировке. Чтобы предотвратить блокировку клиента, вы должны использовать уровень изоляции READ UNCOMMITTED
, но вам нужно будет учитывать возможность получения записей, которые были обновлены/вставлены открытой транзакцией (то есть они могут исчезнуть, если транзакционные ролики назад).
Ответ 2
Хороший вопрос, чтобы говорить о транзакциях.
Ваш основной метод - держать транзакции в совершении. Даже если вы выполняете другие методы, у вас все еще есть блокировки в этой строке. Вы не сможете прочитать эту таблицу с READ COMMITTED, которая ожидается, пока вы не совершите транзакцию блокировки.
Здесь после первого метода возвращается:
![First method returns]()
После возврата второго метода вы добавите еще один замок в таблицу.
![secont method returns]()
Если мы выполним инструкцию select из окна запроса с SPID (55), вы увидите состояние ожидания.
![select is waiting]()
После того, как вы перейдете к основному методу trans commits, вы получите результат выбора select, и он будет показывать только общую блокировку на странице запроса запроса выбора.
![Scope commits and select returns]()
X означает блокировку блокировки, IX. Вы можете прочитать больше из моего сообщения в блоге о транзакциях.
Если вы хотите читать без ожидания, вы можете использовать подсказку nolock. Если вы хотите прочитать после первого метода, вы можете удалить эту внешнюю область.