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


225

用简单的英语来说,使用的缺点和优点是什么

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

在查询.NET应用程序和Reporting Services应用程序时?

Answers:


210

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

为了保持最高级别的隔离,DBMS通常会获取数据锁,这可能会导致并发损失和较高的锁开销。此隔离级别放宽了此属性。

您可能想查看Wikipedia文章上READ UNCOMMITTED的一些示例并进一步阅读。


您可能也有兴趣查看Jeff Atwood的博客文章,内容涉及他和他的团队如何在Stack Overflow的早期解决僵局问题。据杰夫说:

但是nolock危险吗?您能否最终以read uncommittedon 读取无效数据?是的,理论上。您会发现不乏数据库架构宇航员,他们开始对您使用ACID科学,而当您告诉他们您想尝试时,所有人都会发出建筑物火灾警报nolock。的确是这样:该理论令人恐惧。但是我的想法是:“理论上,理论与实践之间没有区别。实际上,两者之间没有区别。”

我永远不建议nolock 您对任何可能出现的数据库死锁问题使用通用的“有益于您的疾病”蛇油修复程序。您应该首先尝试诊断问题的根源。

但是实际上nolock,在您绝对知道的简单查询中添加简单,直接的只读事务似乎永远不会导致问题…… 只要您知道自己在做什么。

READ UNCOMMITTED您可能要考虑的级别的一种替代方法是READ COMMITTED SNAPSHOT。再次引用Jeff:

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


13
作者似乎暗示读未提交/没有锁将返回上一次提交的任何数据。我的理解是未提交读将返回上次设置的任何值,即使来自未提交的事务也是如此。如果是这样,结果将不会“过时几秒钟”检索数据。它将(或者至少如果写入您读取的数据的事务被回滚)正在检索不存在或从未提交过的数据。我错了吗?
xr280xr 2011年

4
好答案。顺便说一句,自从我知道它以来,Oracle默认具有“快照”,这大概是在Sql Server引入它之前的几十年。从SQL Server开始时,我感到非常失望,并且发现所有并发问题都可以通过“原始”锁定机制解决。在Oracle中从未见过“未提交读”。从业者和宇航员一样快乐。
Stefan Steinegger's

13
READ UNCOMMITTED还会使您读取两次行,或者错过整个行。如果在阅读时发生页面拆分,则可能会丢失全部数据块。WITH(NOLOCK)仅在结果的准确性不重要时才应使用
Ian Boyd 2014年

8
@DanielNolan,推荐物品为危险的,因为杰夫不知道什么他在做。读未提交仅对读取数据有意义,永远不会被修改。尝试使用它来读取将要写入的表,这意味着您将在实践中读取会回滚的内容。不仅是您正在读取几秒钟的数据,还....................................。 .......... ‌ ‌ ........................... ....
Pacerier 2014年

5
....................................正在读取从未提交的数据。这就是损坏读取的定义。而且,如果您要基于未提交读操作的结果进行写操作,那么实际上将在写损坏的数据。文章还指出:“在Web应用程序上长大的MySQL与SQL Server相比,开箱即用的悲观性要小得多。” 并非如此,默认情况下,Sql Server在读提交级别上运行,而MySQL在默认情况下在可重复读模式下运行,与未读读间隔五个级别
Pacerier 2014年

36

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

换句话说,只要您将脏读取查询作为估计,并且不基于这些查询做出任何重要决定,结果返回的结果就很好。


36

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

在调试器中启动软件,在逐步执行代码行的同时,它会打开事务并修改数据库。代码停止后,您可以打开查询分析器,将其设置为读取的未提交隔离级别,并进行查询以查看发生了什么。

您也可以使用它来查看长时间运行的过程是否卡住或正确更新数据库。

如果您的公司喜欢制作过于复杂的存储过程,那就太好了。


6
我公司喜欢制作过于复杂的存储过程。解决问题的好主意!
布兰登2015年

22

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

如果您在乎准确性,请不要使用它。

有关MSDN的更多信息:

实现脏读或隔离级别0锁定,这意味着不会发出共享锁,也不会使用独占锁。设置此选项后,可以读取未提交的数据或脏数据。在事务结束之前,可以更改数据中的值,并且行可以在数据集中显示或消失。此选项与在事务中所有SELECT语句中的所有表上设置NOLOCK的作用相同。这是四个隔离级别中限制最小的。


这将如何影响查询速度?
Kip Real 2010年

11
@Kip- select语句不必等待获取由其他事务专门锁定的资源的共享锁。
Jarrod Dixon

15

什么时候可以使用 READ UNCOMMITTED

经验法则

良好:大的汇总报告显示总额不断变化。

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

好消息是,大多数只读报告都属于该“ 好”类别。

更多详情...

可以使用它:

  • 几乎所有面向用户的汇总报告都包含当前的非静态数据,例如年初至今的销售额。它具有误差幅度(可能<0.1%)的风险,该误差幅度远低于其他不确定性因素,例如输入误差或只是精确记录每分钟数据的随机性。

这大概涵盖了商务智能部门在SSRS中所做的大部分工作。当然,前面带有$符号的任何东西都是例外。与为客户提供服务和产生资金所需的相关核心指标相比,许多人对资金的热情更高。(我责怪会计师)。

有风险时

  • 任何下降到详细信息级别的报告。如果需要该详细信息,通常意味着每一行都将与决策相关。实际上,如果您不能在不阻止的情况下提取较小的子集,则可能是由于当前正在对其进行编辑的充分原因。

  • 历史数据。它很少会产生实际的变化,但是尽管用户理解不断变化的数据并不是完美的,但他们对静态数据却感觉不同。脏读不会在这里造成伤害,但双读有时可能会受到伤害。既然您仍然不应该对静态数据进行阻止,为什么还要冒险呢?

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

甚至在OK情况不佳时。

  • 是否有任何应用程序或更新过程利用大笔单笔交易?哪些会删除然后重新插入许多您要报告的记录?在那种情况下,您真的不能NOLOCK在这些表上使用任何东西。

关于报告的要点。实际上,我想到的第一个想法是,read uncommitted当用户看到一些UI网格(数据精度不是很重要)时,是否应该将其用于Web应用程序。用户只想快速浏览一下可能存在的记录,并可能进行一些分页,排序和过滤。仅当用户单击“编辑”按钮时,我才会尝试以更严格的隔离级别读取最新记录。这样的方法在性能方面岂不是更好吗?
JustAMartin 2015年

是的,我认为这是合理的。请记住,更重要的问题是要确保在单击编辑按钮到提交之间没有其他人更改过数据。您可以通过启动事务来获取数据(例如)来解决此问题select item from things with (UPDLOCK)。在其中放置一个快速超时,以便它在无法快速获取锁的情况下告诉用户它正在被编辑。这将确保您不仅对用户而且对开发人员安全。这里唯一的麻烦是您必须开始考虑超时以及如何在UI中处理超时。
Adamantish 2015年

6

关于报告,我们在所有报告查询中都使用了它,以防止查询使数据库陷入困境。我们之所以这样做,是因为我们要提取历史数据,而不是最新的微秒数据。


4

在源极不可能更改的情况下,请使用READ_UNCOMMITTED。

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

当您知道在获取操作期间源可能会更改时,请不要使用READ_UNCOMMITTED。


1
我觉得情况恰好相反。首先,静态数据应无块读取。如果它确实阻止了,那么您现在已经发现有一个重要的挂起的交易问题需要解决。用户也希望将其匹配到小数点后的最后一位,无论他们为去年的年度报告打印了什么。他们通常不希望他们知道不断变化的报告。这不会进行详细的,非常耗时的报告或财务报告,但是如果可以容忍1000个输入错误中的1个,则也是如此READ UNCOMMITTED
精金

TLDR:如果数据不会更改,则因为没有任何阻止,所以您不需要读取未提交的内容。如果您错了并且确实发生了变化,那么阻止用户获取比预期更脏的数据是一件好事。
Adamantish

是的,我倾向于在这里同意@Adamantish-在READ UNCOMMITTED正在积极使用数据的情况下,您可以从大多数情况中受益,并且您希望减少服务器上的负载以避免可能的死锁和事务回滚,因为某些用户不小心滥用了“带有数据网格的网页中的“刷新”按钮。同时查看一堆记录的用户通常不太在乎数据是否有些过时或部分更新。仅当用户将要编辑记录时,才可能需要给他/她最准确的数据。
JustAMartin 2015年

2

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

注意未发生的事情也很有趣。READ UNCOMMITTED不仅忽略其他表锁。它本身也不会引起任何锁定。

考虑您正在生成大型报告,或者正在使用大型(可能是复杂的)SELECT语句从数据库中迁移数据。这将导致在您的交易期间可能将共享锁升级为共享表锁。可以从表中读取其他事务,但是无法进行更新。如果由于其生产数据库可能会完全停止生产数据库,这可能是一个坏主意。

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

因此,如果在执行SELECT时可以插入数据对您来说非常重要,则READ UNCOMMITTED可能有意义。您必须考虑到您的报告可能包含一些错误,但是如果基于数百万行的报告并且在选择结果时仅更新其中的一些错误,则可能“足够好”。您的交易也可能全部失败,因为不能保证一行的唯一性。

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

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


0

它可以用于简单表,例如在仅插入审计表中,其中对现有行没有更新,而对其他表则没有fk。该插入是一个简单的插入,几乎没有回滚的机会。


-7

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

只要您使用“自动增量”字段,然后再多注意一些插入内容,然后再按您的要求,就可以告别阻塞问题。

您可以使用READ UNCOMMITED读取错误,但老实说,确保插入的内容是完整证据非常容易。使用选择结果的插入/更新只是您需要注意的事情。(在此处使用READ COMMITTED,或确保不脏的读取不会引起问题)

因此,进行“脏读”(特别是大型报告),您的软件将运行得更加顺畅...


6
这是非常不准确的,仅涉及到nolock可能出现的问题的表面。页面可能会分裂,联接可能会失败,您可以读取不存在的数据或重复的数据。没有办法使它变得万无一失:在这种隔离级别下,您不能相信任何东西的准确性。READ COMMITTED SNAPSHOT是一种不太危险的“假灵丹妙药”
Mark Sowul 2012年

@MarkSowul对我来说,这个答案的否决似乎不公平。@Clive很清楚,他将切换到Committed插入和更新。至于其他问题,他还提到了使用自动增量键提及页面拆分问题的意识。我同意他的观点,即几乎所有由人类阅读的实时报告都可以忍受最后小数位的细微差异。我同意对于要通过机器读取和转换的详细列表或数据,这是另一回事,Clive也是如此。
精金2014年

1
此评论还表明,对nolock可能带来的问题缺乏充分的了解。“最后的小数位略有差异”几乎无法解决。作为一般建议,即使触及“仅对提交的插入/更新使用只读读取”也是错误的(如果是“如果不存在则插入记录”)。无论如何,“ [[未提交读入]最快,问题最少”是绝对错误的。
Mark Sowul 2014年

根据记录,我同意您的回答,Adamantish:大致准确的汇总,仅此而已。
Mark Sowul 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.