SqlConnection如何管理IsolationLevel?
这篇 MSDN 文章指出:
隔离级别具有连接范围 范围,并且一旦设置为连接 使用设置事务隔离 LEVEL声明,仍然有效 直到连接关闭或 设置另一个隔离级别。当一个 连接关闭并返回 池,隔离级别 最后设置事务隔离级别 声明被保留。随后的 连接重用池化 连接使用隔离级别 当时有效的 连接被池化。
SqlConnection 类没有可以持有隔离级别的成员。那么连接如何知道要在什么隔离级别下运行???
我问这个问题的原因是由于以下情况:
- 我使用 可序列化中的 TransactionScope 模式,说“T1”。
- 打开 T1 的连接。
- T1 已完成/处置,连接 返回连接池。
- 对同一对象调用另一个查询 连接(从获取它之后 连接池)并且此查询运行 在可序列化模式下!!!
问题:
- 池化连接如何仍然存在 知道隔离级别是什么 与之相关???
- 如何将其恢复为其他状态 交易级别???
分辨率:
池连接返回可序列化隔离级别的原因如下:
- 您有一个连接池(假设是 CP1)
- CP1 可能有 50 个连接。
- 您从 CP1 中选择一个连接 C1 并使用 Serialized 执行它。该连接现在已设置其隔离级别。 无论您做什么,都不会重置(除非此连接已关闭) 用于在不同的隔离级别执行代码)。
- 执行查询后,C1(Serializable) 返回到 CP1。
- 如果再次执行步骤 1-4,则使用的连接可能是 C1 之外的其他连接,例如 C2 或 C3。那么,这也将 将其隔离级别设置为可序列化。
- 因此,慢慢地将 Serialzable 设置为 CP1 中的多个连接。
- 当您执行未执行显式隔离级别设置的查询时,从 CP1 选取的连接将决定 隔离级别。例如,如果这样的查询请求连接 CP1使用C1(Serialized)来执行这个查询然后这个查询 即使您没有显式地执行,也会以可序列化模式执行 设置它。
希望能消除一些疑虑。 :)
This MSDN article states that:
An isolation level has connection-wide
scope, and once set for a connection
with the SET TRANSACTION ISOLATION
LEVEL statement, it remains in effect
until the connection is closed or
another isolation level is set. When a
connection is closed and returned to
the pool, the isolation level from the
last SET TRANSACTION ISOLATION LEVEL
statement is retained. Subsequent
connections reusing a pooled
connection use the isolation level
that was in effect at the time the
connection is pooled.
The SqlConnection class has no member that may hold the isolation level. So how does a connection know what isolation level to run in???
The reason I'm asking this is because of the following scenario:
- I opened a transaction using
TransactionScope in Serializable
mode, say "T1". - Opened a connection for T1.
- T1 is finished/disposed, connection
goes back to connection pool. - Called another query on same
connection (after getting it from
connection pool) and this query runs
in serializable mode!!!
Problem:
- How does the pooled connection still
know what isolation level was
associated to it??? - How to revert it back to some other
transaction level???
Resolution:
The reason why pooled connections are returning the serializable isolation level is because of the following reason:
- You have one connection pool (let's say CP1)
- CP1 may have 50 connections.
- You pick one connection C1 from CP1 and execute it with Serializable. This connection has its isolation level set now.
Whatever you do, this will not be reset (unless this connection is
used to execute a code in a different isolation level).- After executing the query C1(Serializable) goes back to CP1.
- If steps 1-4 are executed again then the connection used may be some other connection than C1, let's say C2 or C3. So, that will also
have its isolation level set to Serializable.- So, slowly, Serialzable is set to multiple connections in CP1.
- When you execute a query where no explicit isolation level setting is being done, the connection picked from CP1 will decide the
isolation level. For e.g. if such a query requests for a connection
and CP1 uses C1(Serializable) to execute this query then this query
will execute in Serializable mode even though you didn't explicitly
set it.Hope that clears a few doubts. :)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
隔离级别是在底层 DBMS(例如 SqlServer)中实现的。设置隔离级别很可能会设置 SQL 命令来设置连接的隔离级别。
只要连接保持打开状态,DBMS 就会保持隔离级别。由于连接已放入池中,因此它保持打开状态并保留之前所做的设置。
当搞乱隔离级别时,您应该在任何事务结束时重置隔离级别,或者更好的是在请求新连接时设置它。
Isolation levels are implemented in the underlying DBMS, say SqlServer. Setting the isolation level most probably sets up SQL commands which set the isolation level for the connection.
The DBMS keeps the isolation level as long as the connection stays open. Because the connections is put into the pool, it stays open and keeps the settings made before.
When messing around with isolation levels, you should either reset the isolation level at the end of any transaction, or, even better, set it when a new connection is requested.
SqlConnection.BeginTransaction
接受IsolationLevel
参数,这就是一个控制SqlClient连接的隔离级别。另一种选择是使用通用 System.Transactions 并在TransactionOptions.IsolationLevel
传递给 TransactionScope 构造函数。在 SqlClient 和 System.Transactions 编程模型中,都必须为每个事务显式指定隔离级别。如果未指定,则将使用默认值(对于 SqlClient 来说是已提交读,对于 System.Transactions 来说是可序列化)。池化连接不会盲目重用。它们隐藏了内部成员来跟踪当前状态,例如当前事务、待处理结果等,并且框架可以清理返回到池的连接。仅仅因为状态没有在编程模型中公开,并不意味着不存在(这适用于任何库类,任何类设计者都可以将成员隐藏在
内部
伞)。最后,从池中重用的任何连接都会调用 sp_reset_connection,这是一个服务器过程,用于清除服务器端的会话状态。
SqlConnection.BeginTransaction
accepts anIsolationLevel
argument and this is how one controls the isolation level of SqlClient connections. Another option is to use the generic System.Transactions and specify the isolation level in theTransactionOptions.IsolationLevel
passed to the TransactionScope constructor. Both in the SqlClient and System.Transactions programming model the isolation level has to be explicitly specified for each transaction. If is not specified, the default will be used (Read Committed for SqlClient, Serializable for System.Transactions).Pooled connections are not blindly reused. They have hidden internal members to track the current state like current transaction, pending results etc and the framework can clean up a connection returned to the pool. Just because state is not exposed in the programming model, it doesn't mean is not there (this applies to any library class, any class designer can hide member under the
internal
umbrella).And finally any connection re-used from the pool it invokes
sp_reset_connection
which is a server procedure that cleans up the state of the session on the server side.它不会将隔离级别返回到原始值。使用实体的示例需要一个空事务来重置级别(尽管显然不需要提交(不需要 .Complete())。
尝试在数据库服务器上使用 SP 更改 iso 级别是行不通的。 :
,其中来自链接
并且(没有工作):
It does not return the isolation level to the original value. An example using entities required an empty transaction to reset the level (although it apparentently does not need to be committed (no .Complete() needed).
An attempt to change the iso level using a SP on the DB server does not work. Output:
where from link
and (didn't work):
在SQL Server 2014中,当连接返回池时,池连接的隔离级别会重置。请参阅此论坛帖子
更新 2017-04-22
不幸的是,这后来在 SQL Server 2014 CU6 和 SQL Server 2014 SP1 CU1 中“未修复”,因为它引入了一个错误:
修复: SQL Server 2014中释放SQL Server连接时事务隔离级别重置错误
In SQL Server 2014 the isolation level for pooled connection is reset when connection is returned to pool. See this forum post
Update 2017-04-22
Unfortunately this was later "unfixed" in SQL Server 2014 CU6 and SQL Server 2014 SP1 CU1 since it introduced a bug:
FIX: The transaction isolation level is reset incorrectly when the SQL Server connection is released in SQL Server 2014