SqlConnection如何管理IsolationLevel?

这篇MSDN文章指出:

隔离级别具有连接范围范围,并且一旦设置为与SET TRANSACTION ISOLATION LEVEL语句的连接,它将保持有效,直到连接关闭或设置了另一个隔离级别。 关闭连接并返回到池时,将保留最后一个SET TRANSACTION ISOLATION LEVEL语句的隔离级别。 重新使用池连接的后续连接使用在连接池时生效的隔离级别。

SqlConnection类没有可以保持隔离级别的成员。 那么连接如何知道在哪个隔离级别运行?

我问这个的原因是因为以下情况:

  1. 我在Serializable模式下使用TransactionScope打开了一个事务,比如“T1”。
  2. 打开T1的连接。
  3. T1完成/处理,连接返回连接池。
  4. 在同一连接上调用另一个查询(从连接池获取后),此查询以可序列化模式运行!

问题:

  1. 汇集连接如何知道与之相关的隔离级别?
  2. 如何将其还原回其他一些事务级别???

解析度:
池化连接返回可序列化隔离级别的原因是由于以下原因:

  1. 你有一个连接池(让我们说CP1)
  2. CP1可能有50个连接。
  3. 从CP1中选择一个连接C1并使用Serializable执行它。 此连接现在已设置其隔离级别。 无论您做什么,都不会重置(除非此连接用于执行不同隔离级别的代码)。
  4. 执行查询后,C1(Serializable)返回CP1。
  5. 如果再次执行步骤1-4,则使用的连接可能是除C1之外的其他连接,假设为C2或C3。 因此,它的隔离级别也将设置为Serializable。
  6. 因此,慢慢地,Serialzable在CP1中设置为多个连接。
  7. 当您执行未进行显式隔离级别设置的查询时,从CP1选择的连接将决定隔离级别。 例如,如果此类查询请求连接并且CP1使用C1(Serializable)执行此查询,则此查询将在Serializable模式下执行,即使您未明确设置它。

希望能够澄清一些疑惑。 🙂

隔离级别在底层DBMS中实现,比如SqlServer。 设置隔离级别最有可能设置SQL命令,这些命令设置连接的隔离级别。

只要连接保持打开,DBMS就会保持隔离级别。 因为连接被放入池中,所以它保持打开状态并保持之前进行的设置。

在搞乱隔离级别时,您应该在任何事务结束时重置隔离级别,或者更好的是,在请求新连接时设置隔离级别。

SqlConnection.BeginTransaction接受IsolationLevel参数,这就是控制SqlClient连接的隔离级别的方法。 另一种选择是使用通用的System.Transactions,并在传递给TransactionScope 构造函数的TransactionOptions.IsolationLevel指定隔离级别。 在SqlClient和System.Transactions编程模型中,必须为每个事务显式指定隔离级别。 如果未指定,将使用默认值(SmissClient的Read Committed,System.Transactions的Serializable)。

汇集的连接不是盲目重用的。 他们有隐藏的内部成员来跟踪当前状态,如当前事务,待处理结果等,框架可以清理返回池的连接。 仅仅因为状态未在编程模型中公开,它并不意味着不存在(这适用于任何库类,任何类设计者都可以隐藏internal保护伞下的成员)。

最后,从池中重新使用的任何连接都会调用sp_reset_connection ,这是一个清除服务器端会话状态的服务器过程。

它不会将隔离级别返回到原始值。 使用实体的示例需要一个空事务来重置级别(尽管它显然不需要提交(不需要.Complete())。

尝试使用DB服务器上的SP更改iso级别不起作用。 输出:

之前:ReadCommitted
期间:可序列化
之后:可序列化
通过SP尝试重置后:可序列化
在通过XACT重置期间:ReadCommitted
经过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 

SQL Server 2014中 ,当连接返回到池时,将重置池化连接的隔离级别。 看到这个论坛post

“在SQL 2014中,对于TDS版本为7.3或更高版本的客户端驱动程序,SQL Server会将事务隔离级别重置为池化连接的默认值(读取已提交)。对于TDS版本低于7.3的客户端,它们在针对SQL运行时将具有旧行为2014年”

更新2017-04-22

不幸的是,这在SQL Server 2014 CU6和SQL Server 2014 SP1 CU1中后来“不固定”,因为它引入了一个错误:

FIX:在SQL Server 2014中发布SQL Server连接时,事务隔离级别被错误地重置

“假设您在SQL Server客户端源代码中使用TransactionScope类,并且未在事务中显式打开SQL Server连接。当释放SQL Server连接时,事务隔离级别将被错误地重置。”