【问题标题】:Clustered and covering index ignored on delete statement. Table scan occurs在删除语句中忽略聚集和覆盖索引。发生表扫描
【发布时间】:2012-11-05 14:35:30
【问题描述】:

为什么 SQL Server 2005 会发现执行表扫描比使用主键(且仅主键)上可用的聚集索引更有效?

免责声明还有主键上没有包含列的非聚集、非唯一索引。这让我感到莫名其妙,我们已经在办公室笑得很好。如果这个指数最终成为问题,那么我们就知道该射击谁了。不幸的是,它是一个生产站点,我不能只是把它撕掉,但如有必要,我会制定计划。

也许问题不在于智力缺陷的相反指数,然而……

根据 FogLight PASS,当我们通过主键删除一行时,以下语句会导致每小时扫描约 600 次约 1000 万行的表:

DELETE FROM SomeBigTable WHERE ID = @ID

表格 DDL:

CREATE TABLE [SomeBigTable]
(
    [ID] [int] NOT NULL,
    [FullTextIndexTime] [timestamp] NOT NULL,
    [FullTextSearchText] [varchar] (max) NOT NULL,
    CONSTRAINT [PK_ID] PRIMARY KEY CLUSTERED
    (
        [ID] ASC
    )
) -- ...
ON PRIMARY

聚簇索引约束详解:

ADD CONSTRAINT [PK_ID] PRIMARY KEY CLUSTERED
(
    [ID] ASC 
) WITH  PAD_INDEX = OFF
       ,STATISTICS_NORECOMPUTE = OFF
       ,SORT_IN_TEMPDB = OFF
       ,IGNORE_DUP_KEY = OFF
       ,ONLINE = OFF
       ,ALLOW_ROW_LOCKS = ON
       ,ALLOW_PAGE_LOCKS = ON
       ,FILLFACTOR = 75
ON PRIMARY

同一张表上的非唯一、非聚集索引:

CREATE NONCLUSTERED INDEX [IX_SomeBigTable_ID] ON [SomeBigTable]
(
    [ID] ASC
) WITH  PAD_INDEX = OFF
       ,STATISTICS_NORECOMPUTE = OFF
       ,SORT_IN_TEMPDB = OFF
       ,IGNORE_DUP_KEY = OFF
       ,ONLINE = OFF
       ,ALLOW_ROW_LOCKS = ON
       ,ALLOW_PAGE_LOCKS = ON
       ,FILLFACTOR = 98
ON PRIMARY

也有在 [ID] 列上的外键约束指向一个同样大的表。

600 次表扫描约占使用相同语句对该表每小时执行的总删除操作的 4%。因此,并非所有此语句的执行都会导致表扫描。

这是不言而喻的,但无论如何都要说...这是我想发送包装的很多讨厌的 I/O。

【问题讨论】:

  • sql server 是扫描聚集索引还是非聚集索引?
  • 两者都不是。它正在扫描桌子。
  • 只要表上有聚集索引,我认为 sql 不应该进行表扫描。它应该扫描聚集索引。也许有人删除了聚集索引并将表变成了一个堆?
  • 如果是这种情况,这将是 FogLight 的误报。聚集索引是否仍然存在。

标签: sql sql-server-2005 database-design indexing database-scan


【解决方案1】:

您是否尝试过在桌面上重新计算 statistics 并清除您的 proc cache? 例如像这样:

USE myDatabase;
GO

UPDATE STATISTICS SomeBigTable;
GO

DBCC FREEPROCCACHE
DBCC DROPCLEANBUFFERS

可能是 sql server 只是使用了错误的索引,因为当表中有不同的数据时,它缓存了一个错误的计划。

【讨论】:

  • 在发现完整样本问题前约 2 天重新计算了统计数据。我继续手动更新它们,看看我是否可以在没有成功的情况下启动它。我可以与客户一起安排清除 proc 缓存以查看行为是否发生变化。我对 proc 缓存的了解是,由于这些删除语句的活动,其他语句正在被刷新。
【解决方案2】:

有些事情要尝试,有些事情要检查:

您是否正在运行DELETE SomBigTable where ID = @Parameter 语句?如果是这样,@Parameter 是 int 类型,还是与被删除的列不同的数据类型? (可能不是,但我曾经遇到过一个字符串被转换为 unicode 的情况,这导致索引被忽略。)

制作一个数据库的副本并搞乱它:

  • 尝试确定哪些删除会导致扫描,哪些不会
  • 是否与FK相关表中数据的存在或不存在有关?
  • 外键是否受信任(通过 sys.foriegn_keys 检查)
  • 放下 FK。它有什么改变吗?
  • 删除第二个索引。这有什么改变吗?

可能都不是这些,但在与它们混在一起时,您可能会偶然发现真正的问题。

【讨论】:

  • 如果您无法获得实时数据的副本,请使用(或制作)虚拟/测试数据。
  • @Parameter 实际上是一个 INT,因为它被传递给执行删除的存储过程。那里没有什么时髦的。好东西要检查。您的意思是在删除语句中排除 FROM 吗?该声明与呈现的完全相同。我将模拟一些数据,因为从我们的客户那里检索 300+GB 的数据库进行测试需要几周的时间。您的所有项目符号在测试和外键方面都非常出色。
  • 在 T-SQL 中,在 DELETE 语句中,“from”(如 DELETE from MyTable... 中的)是可选的,所以懒惰的我懒得输入它。
猜你喜欢
  • 2019-08-26
  • 1970-01-01
  • 2010-09-06
  • 2015-01-27
  • 1970-01-01
  • 2021-04-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多