为什么使用READ UNCOMMITTED隔离级别?

说白了,使用的缺点和优点是什么

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

在.NET应用程序和报告服务应用程序的查询中?

359943 次浏览

它的优点是在某些情况下可以更快。缺点是结果可能是错误的(还没有提交的数据可能会返回),并且不能保证结果是可重复的。

如果你在乎准确性,就不要用这个。

有关MSDN的更多信息:

实现脏读或隔离级别0锁定,这意味着不发布共享锁,也不使用排他锁。当设置此选项时,可以读取未提交或脏数据;在事务结束之前,数据中的值可以更改,数据集中的行可以出现或消失。此选项与在事务中的所有SELECT语句中的所有表上设置NOLOCK具有相同的效果。这是四个隔离级别中限制最少的一个。

此隔离级别允许脏读。一个事务可以看到其他事务所做的未提交的更改。

为了保持最高级别的隔离,DBMS通常需要对数据进行锁定,这可能会导致并发性损失和较高的锁定开销。此隔离级别将放松此属性。

你可能想要检查关于READ UNCOMMITTED的维基百科文章的一些例子和进一步阅读。


你可能也有兴趣看看Jeff Atwood的博客文章,关于他和他的团队在Stack Overflow早期是如何解决死锁问题的。杰夫说:

但是nolock危险吗?你能结束吗 用read uncommitted读取无效数据?理论上是的。你会 找到不缺乏的数据库 建筑宇航员开始 对你和所有人进行酸性科学研究 可是拉响大楼火警的时候 你告诉他们你想尝试nolock。 这是真的:这个理论很可怕。但 我是这么想的:“理论上是这样的 理论和理论没有区别吗 练习。实际上是有的。" < / p > 我绝不会推荐使用nolock 就像一般的"善解人意" 蛇油修复任何数据库 您可能会遇到死锁问题。你 是否应该尝试诊断源头 第一个问题。

但在实践中,将nolock添加到你绝对知道是简单、直接的只读事务的查询中似乎从来不会导致问题……只要你知道自己在做什么。

你可能想要考虑的READ UNCOMMITTED级别的一个替代方案是READ COMMITTED SNAPSHOT。再次引用杰夫的话:

快照依赖于一种全新的数据变化跟踪方法…不仅仅是轻微的逻辑更改,它还要求服务器以不同的物理方式处理数据。一旦启用了这个新的数据更改跟踪方法,它就会为每个数据更改创建一个副本或快照。通过在争用时读取这些快照而不是实时数据,读取时不再需要共享锁,并且整体数据库性能可能会提高。

这对于查看长插入查询的进度,做出任何粗略的估计(如COUNT(*)或粗略的SUM(*))等都很有用。

换句话说,脏读查询返回的结果是好的,只要您将它们视为估值,并且不基于它们做出任何关键决策。

我现在总是使用READ UNCOMMITTED。速度快,问题少。当使用其他隔离时,你几乎总是会遇到一些阻塞问题。

只要你使用自动递增字段,多注意一下插入,你就可以和阻塞问题说再见了。

你可以用READ uncommitted犯错误,但老实说,确保你的插入是完全证明是很容易的。使用选择结果的插入/更新只是你需要注意的事情。(此处使用READ COMMITTED,或确保脏读不会引起问题)

所以去脏读吧(特别是大报告),你的软件会运行得更流畅……

这将为您提供脏读,并显示尚未提交的事务。这是最明显的答案。我不认为仅仅为了加快阅读速度而使用这个方法是个好主意。如果使用良好的数据库设计,还有其他方法可以做到这一点。

注意到没有发生什么也很有趣。READ UNCOMMITTED不仅忽略其他表锁。它自己也不会产生任何锁。

假设您正在生成一个大型报告,或者正在使用一个大型且可能复杂的SELECT语句将数据迁移出数据库。这将导致在事务期间将共享锁升级为共享表锁。其他事务可以从表中读取数据,但是不可能进行更新。如果它是一个生产数据库,这可能是一个坏主意,因为生产可能完全停止。

如果使用READ UNCOMMITTED,则不会在表上设置共享锁。你可以从一些新的事务中获得结果,也可以不从表中插入数据的位置和SELECT事务读取的时间中获得结果。如果发生页面分割,您还可能获得相同的数据两次(数据将被复制到数据文件中的另一个位置)。

因此,如果在执行SELECT操作时可以插入数据对你来说非常重要,READ UNCOMMITTED可能是有意义的。您必须考虑到您的报告可能包含一些错误,但如果它基于数百万行,并且在选择结果时只更新了其中的少数行,那么这可能是“足够好”的。由于不能保证行的唯一性,您的事务也可能一起失败。

更好的方法可能是使用SNAPSHOT ISOLATION LEVEL,但您的应用程序可能需要进行一些调整才能使用它。其中一个例子是,如果您的应用程序对一行采用排他锁以防止其他人读取它,并在UI中进入编辑模式。快照隔离级别也会带来相当大的性能损失(特别是在磁盘上)。但是你可以通过硬件来解决这个问题。:)

您还可以考虑恢复数据库的备份,以用于报告数据或将数据加载到数据仓库中。

它可以用于简单的表,例如仅插入的审计表,其中没有对现有行进行更新,也没有对其他表进行fk。插入是一个简单的插入,没有或很少有回滚的机会。

我最喜欢的read uncommited用例是调试事务中发生的事情。

在调试器下启动软件,当您逐步执行代码行时,它会打开一个事务并修改数据库。当代码停止时,你可以打开一个查询分析器,在读取未提交隔离级别上设置并进行查询以查看正在发生什么。

你也可以使用它来检查长时间运行的过程是否卡住,或者使用count(*)查询正确更新数据库。

如果您的公司喜欢创建过于复杂的存储过程,那么这是很好的选择。

在源不太可能改变的情况下使用READ_UNCOMMITTED。

  • 读取历史数据时。例如,两天前发生的一些部署日志。
  • 再次读取元数据时。例如,基于元数据的应用。

当你知道在获取操作过程中源可能会改变时,不要使用READ_UNCOMMITTED。

关于报告,我们在所有报告查询上使用它,以防止查询陷入数据库。我们可以这样做,因为我们提取的是历史数据,而不是精确到微秒的数据。

什么时候可以使用READ UNCOMMITTED?

经验法则

:大的汇总报告显示不断变化的总数。

有风险的:几乎所有其他东西。

好消息是,大多数只读报告属于 good 类别。

更多细节…

可以使用它:

  • 几乎所有面向用户的当前非静态数据的汇总报告,例如年迄今的销售额。 它冒着误差幅度的风险(也许<0.1%),这远低于其他不确定性因素,如输入错误或只是何时准确记录数据的随机性

这可能涵盖了商业智能部门在SSRS中所做的大部分工作。当然,任何前面有$符号的东西都是例外。许多人对金钱的热情远远超过了服务客户和产生金钱所需的相关核心指标。(我责怪会计)。

当风险

  • 任何深入到细节层面的报告。如果需要这个细节,通常意味着每一行都与一个决定相关。事实上,如果您无法在不阻塞的情况下提取一个小子集,可能是因为它正在被编辑。

  • 历史数据。它很少有实际的区别,但尽管用户明白不断变化的数据不可能是完美的,但他们对静态数据却没有同样的感觉。脏读在这里不会造成伤害,但双重阅读偶尔会造成伤害。既然你不应该在静态数据上有块,为什么要冒险呢?

  • 几乎所有提供给具有写功能的应用程序的东西。

即使是OK场景也不是OK。

  • 是否有应用程序或更新过程使用大型单个事务?那些删除然后重新插入大量你正在报告的记录的?在这种情况下,你真的不能在这些表上使用NOLOCK