Sql server &引用;将事务隔离级别设置为“读取未提交”;不吃?还是我看错了方向?
我们有一个问题,一些数据库代码显然执行错误的隔离级别。在代码的这一特定部分中,它应该使用“readuncommitted”执行,以最小化锁。在这一点上,不一致的数据是可以的 然而,代码实际上是用readcommitted读取的,我们不知道为什么 以下是我们所做的:Sql server &引用;将事务隔离级别设置为“读取未提交”;不吃?还是我看错了方向?,sql-server,transactions,isolation-level,read-uncommitted,Sql Server,Transactions,Isolation Level,Read Uncommitted,我们有一个问题,一些数据库代码显然执行错误的隔离级别。在代码的这一特定部分中,它应该使用“readuncommitted”执行,以最小化锁。在这一点上,不一致的数据是可以的 然而,代码实际上是用readcommitted读取的,我们不知道为什么 以下是我们所做的: 打开连接 在此连接上执行“设置事务隔离级别读取未提交” 击中断点 执行SQL 在断点上,我们向数据库发出以下命令: select s.session_id, s.transaction_isolation_level, st.text
select s.session_id, s.transaction_isolation_level, st.text from sys.dm_exec_sessions s
inner join sys.sysprocesses sp on (sp.spid = s.session_id)
CROSS APPLY sys.dm_exec_sql_text(sp.sql_handle) st
此SQL现在报告4个池连接,其中一个是我们可以跨过断点执行SQL的连接,该连接具有以下状态:
53 2 SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
会话53具有隔离级别2(读取已提交),在此会话上执行的最后一个SQL是“SET TRANSACTION…”命令
这怎么可能
我们使用SQL Profiler验证了该连接在.NET代码打开之前不存在,因此没有从连接池中重用它
然而,如果有一个新的连接,并且在该连接上执行的唯一的也是第一个SQL显式地告诉它使用readuncommitted,那么该连接如何仍然是READ-COMMITTED的呢
我们应该看什么
连接字符串(已编辑位)如下所示:
SERVER=hostname;DATABASE=dbname;Integrated Security=false;USER ID=sa;PASSWORD=****;Application Name=appname;Type System Version=SQL Server 2000;Workstation ID=hostname;
var conn = new SqlConnection(...);
conn.StateChance += connection_StateChange;
private void connection_StateChange(Object sender, StateChangeEventArgs e)
{
if (e.CurrentState == ConnectionState.Open)
{
using (IDbCommand cmd = ((SqlConnection)sender).CreateCommand())
{
cmd.CommandText = "SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED";
cmd.ExecuteNonQuery();
}
连接是正常的SqlConnection
连接,以正常方式打开
不幸的是,如果我们编写打开SqlConnection的普通代码,我们就无法重现这个问题,因此应用程序状态一定有问题,但是因为SqlProfiler和Sql Server都告诉我们,是的,Sql已经执行了,但是不,我不在乎
什么会影响这一点
完全相同的代码也会打开其他连接,也就是说,代码会执行多次并打开许多连接,因此池中会有多个连接,但只有第一个连接会出现此问题
这是SQL Server 2008 R2,我们也在2012年重现了此问题
编辑
好的,更多的信息
首先,我们正在启用池,或者更确切地说,我们没有显式地禁用它,也没有旋转连接字符串来创建“N”个池
但是,此连接是第一个使用此特定连接字符串打开的连接,因此不会从池中检索它。另请参阅下面我关于它永久“生病”的说明
此连接的设置方式如下:
SERVER=hostname;DATABASE=dbname;Integrated Security=false;USER ID=sa;PASSWORD=****;Application Name=appname;Type System Version=SQL Server 2000;Workstation ID=hostname;
var conn = new SqlConnection(...);
conn.StateChance += connection_StateChange;
private void connection_StateChange(Object sender, StateChangeEventArgs e)
{
if (e.CurrentState == ConnectionState.Open)
{
using (IDbCommand cmd = ((SqlConnection)sender).CreateCommand())
{
cmd.CommandText = "SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED";
cmd.ExecuteNonQuery();
}
在此之前,我们没有执行任何其他SQL
请注意,此代码在应用程序的生命周期中被多次使用,只有它打开的第一个连接最终出错
这种联系也会永久性地生病。因为每次我们打开连接时(即使我们可能会将其从连接池中取出),都会执行上面的状态更改事件,试图再次设置隔离级别。这也会失败,但仅限于此单一连接
此外,自从我发布这个问题以来,我们发现了一个影响这一点的因素
通过更改我在上面发布的连接字符串:
...;Type System Version=SQL Server 2000;...
为此:
...;Type System Version=SQL Server 2008;MultipleActiveResultSets=true;...
然后这个问题消失了,在前面列出的断点处,连接现在处于“readuncommitted”状态
这是一个转移视线的问题,在我们实际执行代码之前,我们的概述中不再报告该连接
我们正在继续调试。这里的问题是,不接受参数的默认值为read committed。我猜我们不明白该页面上的“默认隔离级别”文本是什么 该页面包含以下文本: 如果未指定隔离级别,则使用默认隔离级别。要使用BeginTransaction方法指定隔离级别,请使用采用iso参数(BeginTransaction)的重载。为事务设置的隔离级别在事务完成后一直保持,直到连接关闭或释放。在未启用快照隔离级别的数据库中将隔离级别设置为快照不会引发异常。事务将使用默认隔离级别完成 (我的亮点) 下面是一个LINQPad脚本,它演示了:
void Main()
{
using (var conn = new SqlConnection("Data Source=.;Initial Catalog=master;Integrated security=true"))
{
conn.Open();
Dump(conn, "after open");
using (var cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = "SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED";
cmd.ExecuteNonQuery();
}
Dump(conn, "after set iso");
using (var cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = "BEGIN TRANSACTION";
cmd.ExecuteNonQuery();
}
Dump(conn, "after sql-based begin transaction");
using (var cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = "COMMIT";
cmd.ExecuteNonQuery();
}
Dump(conn, "after sql-based commit");
var trans = conn.BeginTransaction();
Dump(conn, "after .net begin transaction", trans);
trans.Commit();
Dump(conn, "after .net commit");
}
}
public static void Dump(SqlConnection connection, string title, SqlTransaction transaction = null)
{
using (var cmd = new SqlCommand())
{
cmd.Connection = connection;
if (transaction != null)
cmd.Transaction = transaction;
cmd.CommandText = "SELECT transaction_isolation_level FROM sys.dm_exec_sessions WHERE session_id = @@SPID";
Debug.WriteLine(title + "=" + Convert.ToInt32(cmd.ExecuteScalar()));
}
}
它将输出:
after open=2
after set iso=1
after sql-based begin transaction=1
after sql-based commit=1
after .net begin transaction=2
after .net commit=2
在这里,您可以看到,通过SQL手动启动和提交事务不会更改隔离级别,但是在.NET中启动事务而不显式声明隔离级别仍然会将其更改为read committed
因为我们读到的每一个地方,在没有明确说明隔离级别的情况下启动一个事务都会说它继承了会话的隔离级别,我想我们不明白.NET不会这样做。我复制了上面的代码并打开了连接,我得到了读取未提交连接的预期结果。必须有其他代码设置您的隔离级别可能有什么东西在此连接上将
NOEXEC
设置为?如果我在一个批中执行此操作,然后在一个查询窗口的下一批中运行设置事务隔离级别READ UNCOMMITTED
,我得到了您显示的输出-最后执行的命令是SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
,但是TRANSACTION\u ISOLATION\u LEVEL
列仍然显示它设置为2。请看我自己的答案,我不相信我已经在我的问题中发现了所有必要的代码片段,所以我不想把它留在这里,老实说,我觉得这不是一个好问题。我也有同样的问题。难道没有更好的办法吗?打开和关闭交易两次似乎不是一个好主意。