进行未提交的SET事务隔离级别的好处


12

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED在大多数常规SQL查询中使用了大多数方法,主要是因为这是在最初学习该语言时钻到我身上的。

根据我的理解,此隔离级别的行为方式与WITH (NO LOCK)我曾经倾向于使用的方式相同SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

  • 有没有时间我应该WITH (NO LOCK)用完SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
  • 是否 SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED阻止其他用户被锁定在我正在读取的表之外?
  • 如果 SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED用于停止锁,但是我仅在读取数据,那么使用它有什么意义?只是系统密集型查询会生成锁吗?在运行将在5到10秒内返回的查询时,是否值得使用它?
  • 我被告知不要 SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED在读取将用于更新的数据时使用,大概是为了避免更新脏数据。这是唯一的原因吗?
  • 对于我正在处理的数据库类型,有一个生产和测试环境。我们很少会查询生产环境,但是在需要时,通常会SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED在查询中使用它。我知道这样做可能会导致脏读。除了接收可能不会最终提交给数据库的数据(因此将我的结果排除在外)之外,还有哪些其他类型的“脏读”是可能的?

很抱歉出现大量问题。


2
您可能会两次读取相同的数据,这是另一个陷阱。使用RU或NO LOCK作为标准不是一个好主意。
詹姆斯·安德森

9
我不会READ UNCOMMITTED在任何地方都使用,就像我不会WITH (NOLOCK)在所有地方都使用(本质上是同一件事)一样,使用blogs.sqlsentry.com/aaronbertrand/bad-habits-nolock-everywhere
Mark Sinkinson

1
查看SNAPSHOT隔离模型。它们比RCU强大得多,也不会阻塞或引起阻塞。对于您来说,它们听起来像是一个很好的默认模型(而不是默认为RCU!)。
usr

Answers:


26

太糟糕了,您是这样学习的(对不起!)。

READ UNCOMMITTED让我们阅读每一行,是的。即使是那些谁在目前使用INSERTUPDATEDELETE操作。如果您需要快速查看某些数据或在关键任务SELECT语句中的声明,这些语句将非常有害,这将非常有用。

实际上,您冒着诚信的风险。您可能会读到当前用于删除或更改的行。还可能显示您读取了错误的值。这可能确实不常见,但有可能发生。那是什么意思 好吧,想想一个非常宽的行(它有许多列,许多长nvarchar列)。此行将进行更新并设置新值。在极少数情况下,您可能只读一半行。例如,如果用户更改其登录值,则可能会发生另一件事。他更改了他的邮件+密码。邮件已设置,但密码未设置。这样,您将具有不一致的状态。

我建议忘记READ UNCOMMITTED。只需在真正需要的地方使用它即可。

另一个选择是启用READ_COMMITTED_SNAPSHOT数据库选项- READ COMMITTED SNAPSHOT由于tempdb中启用了行版本控制,因此可以使用该选项。这样,您就可以读取行的另一个(旧)版本。这不会阻止您的查询。但是您可能也会读取一个旧值,但是读取的是一个一致的旧值。

可以用另一个想法WITH(READPAST)代替WITH(NOLOCK)。您将读取表的旧状态(有点像中的SNAPSHOT ISOLATION),但是将跳过所有当前锁定的行。


@Ionic,非常感谢您的回复!我非常感谢您的帮助。
dmoney 2015年

@HingeSight,听起来很像,但是很有可能这是我对声明的解释,谢谢您的输入。
dmoney 2015年

1
SNAPSHOT ISOLATION无需启用即可打开READ COMMITTED SNAPSHOT。仅READ COMMITTED SNAPSHOT需要启用数据库选项,以便行版本控制而不是锁定以保持读取一致性,从而避免了脏读取的需要。
丹·古兹曼

是的,我的意思是@DanGuzman。抱歉,当时的答案还不清楚。我编辑了 :-)
Ionic

使用READPAST时,您将跳过已锁定的记录,而不会获得旧的值-因此,我知道可以使用的唯一地方是队列处理
James Z

2

如已接受的答案所述,请避免使用READ UNCOMMITTED隔离级别(除非真正需要时使用),否则可能会读取错误的数据。但是要回答问题中的第三个要点,我发现有两种情况很有用:

  1. 在编写和测试SQL Server SSIS程序包时,程序包的步骤可能包含在事务中。如果要通过在SSIS调试器中逐步运行程序包来测试程序包,则可能要在表上有锁的情况下检查表。使用SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED可以在调试包时使用SQL Server Manager Studio检查表。

  2. 在SQL Server Manager Studio中,您可能需要通过将T-SQL代码包装在事务中来测试T-SQL代码,以便您选择回滚更改。例如,在测试数据库上,您可能希望将数据还原到初始状态,作为测试的一部分。如果您正在测试事务包装的代码,并且想要在事务进行过程中检查锁定的表,则可以使用SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED来检查另一个窗口中的值。

我接受这些对于READ UNCOMMITTED来说是相当晦涩的用法,但是我发现它们在测试环境中很有用。


1

在大多数数据库中,绝大多数活动,甚至是插入,删除和更新,都在显式事务之外。

当然,READ UNCOMMITTED在这些情况下,(脏读)可能会提供错误的信息,但是该信息会在5秒钟前更正。

脏读产生真正糟糕的结果的时间是:事务失败且必须回滚,或者对通常使用显式事务一起更新的两个表运行查询时。

同时,在典型的大型繁忙数据库中,其读写比率为100(或更多)与1的比率,每个不使用脏读的单个(读取)查询都会减慢系统速度,因为它需要获取和检查锁,这使事务失败的可能性更大(通常是由于死锁引起的),这可能导致更严重的数据库完整性问题。

取而代之的是,使用脏读使系统更快速,更可靠(部分原因是由于性能提高,不一致的可能性较小)。

当然,有时候不应该使用它们。我不喜欢将数据库默认设置为使用READ UNCOMMITTED隔离级别,甚至不使用SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTEDSQL脚本和SP开头的显式语句-过多的机会会在不应该进行脏读取的情况下发生。相反,(NOLOCK)提示是一种更好的方法,因为它们明确表明程序员考虑了自己在做什么,并决定对于该特定查询中的特定表,脏读是安全的。

如果您正在编写应用程序以将钱从一个银行帐户转移到另一个银行帐户,则不应使用“脏读”。但是大多数应用程序不需要那种级别的偏执狂。

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.