情况:Azure SQL Server 数据库(20 个 DTU)使用了大约 20% 的空间。没有其他进程正在运行查询,计算利用率图表在 0%.
上显示一条实线我们有一个大约有 80 列的空表。没有创建索引。我们有这样的查询
SELECT column1,
column2,
...
columnN
FROM table
WHERE column1 = "some"
AND column2 = "something"
AND column3 = "something"
AND column4 = "something"
在
WHERE
子句上,我们使用了 4 列(其中一列是 Timestamp
列)。
当我们运行这个查询时,运行几分钟后,我们终止进程,因为它永远不会结束,数据库 CPU 为 100%。正如我所说,桌子是空的。
我创建了一个这样的非聚集索引:
CREATE NONCLUSTERED INDEX [index_name]
ON [dbo].[table] ([column1] ASC, [column2] ASC,
[column3] ASC, [column4] ASC)
WITH (STATISTICS_NORECOMPUTE = OFF, DROP_EXISTING = OFF,
ONLINE = OFF, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO
创建索引后,相同的查询需要几分钟才能完成(同样,CPU 使用率为 100%)。查询执行计划只是显示一个
table scan
。我的第一个问题是,为什么我们得到 table scan
而不是 index scan
?该索引包含 WHERE
子句中包含的 4 列。
但是,如果我创建相同的索引但作为
CLUSTERED
索引,查询几乎立即完成并且执行计划显示 index clustered seek
.
为什么对空表的查询需要聚集索引来得出空表的结论?为什么有一个非集群的还不够?
任何帮助将不胜感激。
更新。
带有非聚集索引的执行计划。注意:因为我已经创建了一个聚簇索引并将其删除以显示此执行计划,所以现在它不再是索引扫描了。这是索引查找。所以看起来问题确实是堆问题。
创建聚簇索引后的执行计划是here.
估计的计划没有证实这一点,但这几乎可以肯定是这样一种情况,即堆曾经很大并且仍然持有许多页面,这些页面自从这些行被删除后还没有被释放。
这种症状在堆中尤为普遍,正如 Martin 指出的那样,Paul Randal 在这里写道:
添加聚簇索引解决了这个问题,因为它必须完全重建表,然后才能最终释放空页。即使您删除聚簇索引并添加非聚簇索引,症状仍然会消失,因为那些被释放的页面永远消失了。但是,如果您保留 just 非聚集索引,并随着时间的推移填满表,然后再次清除它,您将再次遇到同样的问题。
Only 添加非聚集索引(不首先创建聚集索引或以其他方式强制重建)并不能解决问题,因为它不像添加聚集索引那样重建表。所以页面仍然没有被释放。
您还可以通过删除空表并重新创建它(如果它实际上是空的)来解决症状,或者 - 无论它是否为空 - 手动重建它:
ALTER TABLE dbo.MyHeap REBUILD;
但是,为了完全防止症状发生,我只是不会使用堆。创建聚簇索引并保留它.