我昨天花了几个小时,今天试图解决为什么一个特定的数据库查询需要很长时间(3分钟以上)才能在我在本地实例上恢复的数据库上执行,但它只需要几分之一秒在生产服务器上。 (它不仅仅是生产服务器具有更高规格的问题,因为之前我的本地实例上的相同查询已经正常执行)。
我将尝试以最好的方式解释问题 - 意味着帖子可能有点长。查询非常简单:
SELECT [t0].[BillId], [t0].[CustomerId], [t0].[BillDate], [t0].[DueDate],
[t0].[Period], [t0].[Total], [t0].[Balance], [t0].[DateCreated], [t0].[DateModified] --, other fields
FROM [dbo].[Bills] AS [t0]
WHERE ([t0].[Period] = 201307) AND (([t0].[DateModified] >= '2013-07-04 17:00:00.000')
OR (([t0].[DateModified] < '2013-07-04 17:00:00.000') AND (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[BillDetails] AS [t1]
WHERE [t1].[BillID] = [t0].[BillID] AND [t1].[DateModified] >= '2013-07-04 17:00:00.000'
))))
上述查询返回与特定时间段相关联且自特定时间点以来已修改的帐单。如果帐单的 DateModified 字段中的值晚于指定日期,或者相关子行中的 DateModified 字段上的值(BillDetails
)比指定日期晚。我将在参考上述查询返回的结果时使用“delta”一词。
它有点奇怪,但是在我的本地实例上,当没有delta时,即当Bills
和BillDetails
没有变化时,执行此查询似乎有点阻止最后指定日期。当返回delta时,查询只需要几分之一秒。在实时/生产数据库中,查询对两种情况的响应都非常快。
我曾经遇到过这个问题,但我没有记录我是如何修复它的 - 很可能是因为我无法弄清楚是什么修复了我尝试过的很多东西。在此期间,生产数据库中发生了“阻塞”,而我的本地实例很好。我怀疑它与索引有关。
当它现在在我的本地实例上重现时,索引是主要的嫌疑人。我运行了我用于重组索引的实用程序脚本,平均碎片率在5%到30%之间,并且重建索引的平均碎片超过30%。这似乎没有解决问题。我已经尝试了很多东西,但修复它的是删除并在Bills
表上重新创建非聚集索引。
我在Bills
表上有三个非聚集索引,定义如下(或多或少):
NCI_IDX1 ([CustomerId] ASC)
NCI_IDX2 ([CustomerId] ASC, [Period] ASC, [BillId] ASC)
NCI_IDX3_Inc_Various ([Period] ASC, [DateModified] ASC, [BillId] ASC, [CustomerId] ASC) INCLUDE ([BillDate], [Total], [Balance] /* more fields */)
根据我在尝试解决问题时的经验,我有几个问题啃着我的脑海。
问:重建索引有什么作用?它是否相当于手动删除和重新创建索引
问:删除索引会导致与删除索引相关联的统计信息。重建索引是“刷新”还是“重置”相关统计信息?
问:在选择执行计划时,鉴于查询优化器对统计信息的依赖,统计数据是否可能导致查询执行的阻塞?
问:根据MSDN,无法使用DROP STATISTICS
删除有关索引的统计信息。这是否意味着必须删除并重新创建索引才能重置统计信息?
注意:标记LINQ和C#,因为上述查询的来源是数据层中使用LINQtoSQL的C#应用程序。
答案 0 :(得分:1)
但是,索引重新组织根本不会更新统计信息 因为整个表格不是一次性分析的(只有页面 碎片重组了。)
在这篇文章中:http://www.sqlskills.com/blogs/kimberly/the-accidental-dba-day-15-of-30-statistics-maintenance/
也许你的脚本重建/重组索引只是重新组织索引,但没有重建它(由于碎片很少),所以这个索引的统计数据变成了“老”。
文章中有一个很好的建议:
因此,如果您发现索引维护脚本是定期的 重新组织索引,然后你要确保你也添加 在统计维护中。