在数据仓库场景中使用 WITH(NOLOCK) 有啥缺点吗

Posted

技术标签:

【中文标题】在数据仓库场景中使用 WITH(NOLOCK) 有啥缺点吗【英文标题】:In a Data Warehouse scenario is there any disadvantage to using WITH(NOLOCK)在数据仓库场景中使用 WITH(NOLOCK) 有什么缺点吗 【发布时间】:2009-06-18 20:22:49 【问题描述】:

我有一个 Kimball 风格的 DW(星型模型中的事实和维度 - 没有迟到的事实行或列,除了作为类型 2 缓慢变化维度的一部分过期外,没有列的维度发生变化),每天需要处理繁重的插入和更新行(在新日期)以及每月和每日报告流程。事实表按日期分区,以便于旧数据的滚动。

我了解WITH(NOLOCK) 可能会导致读取未提交的数据,但是,我也不希望创建任何会导致 ETL 进程失败或阻塞的锁。

在所有情况下,当我们从 DW 中读取数据时,我们正在从事实表中读取一个不会改变的日期(事实表按日期分区)和维度表,这些数据表的属性不会因为它们的事实而改变链接到。

那么 - 有什么缺点吗? - 可能在执行计划中或在此类SELECT 的操作中-仅在同一张表上并行运行的查询。

【问题讨论】:

相关。 ***.com/questions/20047/… 【参考方案1】:

这可能是你需要的:

`ALTER DATABASE AdventureWorks 设置 READ_COMMITTED_SNAPSHOT 开启;

ALTER DATABASE AdventureWorks 设置 ALLOW_SNAPSHOT_ISOLATION 开启; `

然后继续使用

SET TRANSACTION ISOLATION LEVEL READ COMMITTED

在您的查询中。根据 BOL:

READ COMMITTED 的行为取决于 READ_COMMITTED_SNAPSHOT 数据库选项的设置:

如果 READ_COMMITTED_SNAPSHOT 设置为 OFF(默认值),则数据库引擎使用共享锁来防止其他事务在当前事务正在运行读取操作时修改行。共享锁还阻止语句读取其他事务修改的行,直到其他事务完成。共享锁类型决定何时释放。在处理下一行之前释放行锁。读取下一页时释放页锁,语句完成时释放表锁。

如果 READ_COMMITTED_SNAPSHOT 设置为 ON,则数据库引擎使用行版本控制为每个语句提供数据的事务性一致快照,因为它在语句开始时就存在。锁不用于保护数据不被其他事务更新。

希望对您有所帮助。 拉杰

【讨论】:

我会考虑的。连接上还有其他操作(即我的流程配置和状态数据,而不是事实/暗星建模数据),我不一定希望这样做。【参考方案2】:

只要都是无更新数据就没有坏处,但如果有很多好处,我会感到惊讶。我会说值得一试。最糟糕的情况是,如果您处于批量插入的中间,您将获得不完整和/或不一致的数据,但您可以决定这是否会使任何有用的东西失效。

【讨论】:

我们正在读取的事实行不会改变,维度行将始终有效,但它们可能已过期并为新事实创建一个新维度。 对我来说似乎很直接。我只有两个问题。 1. 如果不进行此更改,其运行方式是否存在问题(即这可能是过早的优化)。 2. 这些都是只读查询,您正在放松它们的隔离级别。你在想象什么坏事(除了结果的明显糟糕,你显然通过强调附加和事实版本来缓解)? 我无法控制 ETL,但我负责所有报告。我无权访问 sp_who,因此我需要在 DBA 抱怨我阻止它们之前主动确保我的所有(重要)处理不会干扰每日和每月的负载。 那么我认为对您的具体问题的简单回答是使用 WITH(NOLOCK) 会减少并可能消除您创建任何会导致 ETL 进程失败或阻塞的锁的可能性;并且这种策略没有任何缺点。您有很多建议可以用更奇特的方式来完成大致相同的事情,但它们通常涉及您可能无权访问的全局服务器设置更改,并且您的 dbas 会怀疑。【参考方案3】:

您是否考虑过为您的 DW 创建一个 DATABASE SNAPSHOT 并从中运行您的报告?

【讨论】:

不,这不太可能,因为我们谈论的是几 TB 的数据。 DW 就是为此目的而设计的,这就是事实表按日期分区的原因。 但是数据库快照是一个具有写时复制语义的稀疏文件。您只需要保留磁盘上的空间,只有在原始数据库上发生写入时才会发生实际的 I/O 操作。【参考方案4】:

是的。您的 SQL 的可读性将大大降低。您将不可避免地错过一些 NOLOCK 提示,因为使用 NOLOCK 策略的 SQL SELECT 命令必须把它放在所有地方。

You can get the same thing by setting the isolation level

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

最后你会获得 10% 的性能提升(抱歉,我太懒了,太懒了,也懒得去查这篇文章,但它就在那里)

我会说 10% 的增益不值得降低可读性。

【讨论】:

+1 用于设置隔离级别,但“无锁”总比没有好。这不是 10% 的事情,它是否会运行。 我会考虑的。连接上还有其他操作(即我的流程配置和状态数据,而不是事实/暗星建模数据),我不一定希望这样做。此时,我将 WITH(NOLOCK) 集中到我使用的视图中,这些视图包装和展平 DW 数据库中的星形模型。【参考方案5】:

如果使整个数据库只读是可能的,那么这是一个更好的选择。无需修改所有代码即可获得未提交读取的性能。

ALTER DATABASE adventureworks SET read_only

【讨论】:

DW 数据库对我的用户来说已经是只读的,但是对于将新数据加载到事实(以及必要的维度)中的 ETL 进程来说,它必须是可写的。我的数据库保存着我的流程和配置。 我认为只有当数据库是只读的而不是用户时才有效。也许考虑将数据库更改为 read_only 作为 ETL 的一部分【参考方案6】:

NOLOCK 执行“脏读”(不雅读 READ UNCOMMITTED 与 NOLOCK 做同样的事情)。如果数据库在您阅读时正在更新,那么您可能会收到不一致的数据。唯一的选择是要么接受锁定并因此阻塞,要么选择 SQL 2005 及更高版本discussed here 中提供的两个新隔离级别之一。

【讨论】:

无法对数据进行插入或更新。唯一的变化将是未来的日期,我们在处理完成之前不会读取这些日期。

以上是关于在数据仓库场景中使用 WITH(NOLOCK) 有啥缺点吗的主要内容,如果未能解决你的问题,请参考以下文章

使用 WITH(NOLOCK) 提高性能

Laravel Eloquent 和 Query Builder “with (nolock)”

在 JOIN 查询中的某些表上使用 WITH (NOLOCK)

WITH (NOLOCK)

with(nolock)的用法

理解with(nolock)(转载)