存储过程中的隔离级别失败

时间:2017-03-09 01:05:40

标签: sql sql-server

我有一个SP大约需要30秒才能运行。这10秒创建一个我操作的初始临时表,例如。

SELECT * INTO #tmp FROM view_name ....(lots of joins and where conditions)

我的理解是,默认情况下,这个长选择(插入)可以锁定表,从而锁定附加到数据库的应用程序吗?

我的解决方案是添加

SET ISOLATION TRANSACTION LEVEL READ UNCOMMITTED;

到存储过程的顶部,然后在结束时将其重置为COMMITED。

我想这是一个两部分的问题,首先是我对这个正确的理解,其次是如果我的SP中的某个地方失败并出现错误。数据库是否仍处于UNCOMMITTED模式?

1 个答案:

答案 0 :(得分:2)

对于从存储过程执行的代码的特定情况,隔离级别将重置为调用存储过程之前的状态,无论发生什么情况。 From the docs

  

如果您在存储过程中发出SET TRANSACTION ISOLATION LEVEL或   触发器,当对象返回控制时,隔离级别被重置   到调用对象时的有效级别。

如果您没有使用存储过程,或者您正在处理存储过程中的代码,那么(更复杂的)故事如下所示。

隔离级别是会话的属性,而不是数据库的属性。是否在错误之后执行SET TRANSACTION ISOLATION LEVEL之类的语句取决于错误是中止整个批处理还是仅中止语句。对此的规则是complicated and unintuitive。为了使事情更有趣,会话的隔离级别为not reset if it's associated with a pooled connection。如果您想要保持隔离级别的万无一失的代码,最好始终从客户端代码设置隔离级别。如果必须在T-SQL中执行此操作,最好使用SET XACT_ABORT ONTRY .. CATCHWITH (NOLOCK)表提示将锁定选项限制为特定表或表。

但为什么让你的生活变得复杂?考虑一下真的是否需要使用READ UNCOMMITTED(或WITH (NOLOCK)),因为you have no guarantee of data consistency anymore when you do,即使你认为你&#39对此,它可以导致结果不是有点错误但完全没用。不幸的是,尤其是当数据库上的负载增加时(这就是为什么你可能会考虑首先减少锁定)。

如果您的查询需要很长时间才能锁定数据,这对应用程序来说是一个问题(两者都不应该假设,因为SQL Server擅长锁定只要有必要的数据),你的第一直觉应该是查看查询本身是否可以优化以减少锁定时间(通过添加索引,最有可能)。如果您仍然无法接受锁定,请考虑使用snapshot isolation。只有在对这些事情进行评估之后,您才应该考虑脏读,甚至只考虑数据一致性不是您主要关注的情况(例如,查询会获得一些经常刷新的监控统计信息)。