【问题标题】:What's blocking "Select top 1 * from TableName with (nolock)" from returning a result?是什么阻止了“使用(nolock)从 TableName 中选择前 1 个 *”返回结果?
【发布时间】:2012-06-08 20:38:13
【问题描述】:

我目前正在运行以下语句

select * into adhoc..san_savedi from dps_san..savedi_record

这需要很长时间,我想看看它走了多远,所以我运行了这个:

select count(*) from adhoc..san_savedi with (nolock)

这并没有及时返回任何东西,所以我做了这个:

select top 1 * from adhoc..san_savedi with (nolock)

即使这似乎无限期地运行。如果有数百万条记录,count(*) 可能需要很长时间,我可以理解,但我不明白为什么考虑到我指定了 nolock,选择前 1 条记录不会立即返回。

以完全公开的名义,dps_san 是一个通过链接服务器从 odbc 连接中提取的视图。我不认为这会影响为什么我不能返回第一行,而是将它扔在那里以防我错了。

所以我想知道是什么阻止了该语句的运行?

编辑:

正如我上面提到的,是的 dps_san..savedi_record 是一个视图。它的作用如下:

select * from DPS_SAN..root.SAVEDI_RECORD

它只是一个别名,没有分组/排序/等,所以我认为问题不在于这里,但如果我错了,请赐教。

【问题讨论】:

  • 视图有什么作用?如果它执行 GROUP BY、ORDER BY 或使用聚合函数,那么选择前 1 行的成本可能几乎与选择所有行一样昂贵。
  • 您确定 SELECT INTO 已将单行写入磁盘了吗?也许它仍处于模式锁定模式,因为它仍在等待 ODBC 从您的链接连接中传递第一行。
  • @AaronBertrand 不,我不确定。但是,我可以打开另一个查询窗口并从 dps_san..savedi_record 中选择我想要的所有记录并获得结果,所以如果它现在还没有写入任何结果(已经 2 多个小时),那就太奇怪了。

标签: sql sql-server-2008 locking


【解决方案1】:

SELECTNOLOCK 的查询实际上并不需要锁定,它们仍然需要在表 (and as it is a heap it will also take a hobt lock) 上使用 SCH-S(模式稳定性)锁定。

此外,在SELECT 甚至可以开始之前,SQL Server 必须为该语句编译一个计划,这还要求它对表进行SCH-S 锁定。

由于您的长期运行事务通过SELECT ... INTO 创建表,它持有一个不兼容的SCH-M 锁定直到语句完成。

您可以通过在阻塞期间查看sys.dm_os_waiting_tasks whilst while 来验证这一点。

当我在一个连接中尝试以下操作时

BEGIN TRAN

SELECT *
INTO NewT
FROM master..spt_values

/*Remember to rollback/commit this later*/

然后执行(或者只是尝试查看估计的执行计划)

SELECT *
FROM NewT
WITH (NOLOCK)

在一秒钟内阅读查询被阻止。

SELECT wait_type,
       resource_description
FROM sys.dm_os_waiting_tasks
WHERE session_id = <spid_of_waiting_task>

显示等待类型确实是SCH_S 和阻塞资源SCH-M

wait_type        resource_description
---------------- -------------------------------------------------------------------------------------------------------------------------------
LCK_M_SCH_S      objectlock lockPartition=0 objid=461960722 subresource=FULL dbid=1 id=lock4a8a540 mode=Sch-M associatedObjectId=461960722

【讨论】:

  • @AaronBertrand - 谢谢,认为“虽然”英国人可以使用according to Wikipedia
  • 这并不意味着我必须喜欢它。英国人也可以使用“while”对吗? :-)
  • @AaronBertrand - 实际上虽然对我来说听起来也更好。我计划扩展我的答案,所以我会同时重新审视它!
  • 只是给你一个很难过的人。我对while有意见,但这并不妨碍我理解你。 :-)
  • @MartinSmith 你的回答很好,但我真的只给了你一个 +1 使用这个词。
【解决方案2】:

很可能没有锁...如果 dps_san..savedi_record 是一个视图,那么它可能需要很长时间才能执行,因为它可能在不使用索引的情况下访问表,或者它可能正在对数百万条记录进行排序,或者任何原因。那么您的查询,即使是简单的 top 或 count,也只会与执行该视图的速度一样快。

【讨论】:

  • 选择是针对正在填充的表,而不是视图。
  • 查看我的编辑。视图只选择 *.没有分组/排序/或任何会导致它需要在返回结果或类似的东西之前扫描整个表。
  • @AaronBertrand 我想他说的和你建议的一样,可能还没有插入任何记录。
【解决方案3】:

这里有几个问题需要考虑。 dps_san..savedi_record 是视图吗?如果是这样,获取数据可能需要很长时间。我能想到的另一件事是您正在尝试使用select into 语法创建一个临时表,这是一个坏主意。 select * into ... 语法将在选择期间锁定 tempdb。

如果您使用该语法创建表,则有一种解决方法。首先,通过在初始语句的末尾抛出 where 1=0 创建表:

select * into ... from ... where 1=0

这将首先创建表(这很快),这允许您insert into,因为该表现在存在(不会在查询期间锁定 tempdb)。

【讨论】:

  • @AaronBertrand 先生,您是对的,谢谢您为我解决了这个问题(忘了它只能用于创建表格)。 Select Into... Reference
  • 谢谢,我试试看。
【解决方案4】:

找到正在执行select intosession_id

SELECT r.session_id, r.blocking_session_id, r.wait_type, r.wait_time
  FROM sys.dm_exec_requests AS r
  CROSS APPLY sys.dm_exec_sql_text(r.plan_handle) AS t
  WHERE t.[text] LIKE '%select%into%adhoc..san_savedi%';

这应该让您知道是否另一个会话正在阻止选择进入,或者它是否具有导致问题的等待类型。

您可以在另一个窗口中为尝试进行选择的会话重复该过程。我怀疑 Martin 是对的,并且我之前关于模式锁定的评论是相关的。

【讨论】:

  • s 应该是 r 我假设。
猜你喜欢
  • 2011-10-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-02
相关资源
最近更新 更多