SQL Server 中的查询性能

Posted

技术标签:

【中文标题】SQL Server 中的查询性能【英文标题】:Query Performance in SQL Server 【发布时间】:2017-09-19 13:05:32 【问题描述】:

我有一个包含超过 1100 万条记录的 SQL Server 表。这些记录按“类别”和“平台”组织。我被以下场景难住了……

SELECT COUNT(*) FROM TableName WHERE Category = 'session' AND Platform = 'windows';
-- Returns 1261500

SELECT COUNT(*) FROM TableName WHERE Category = 'session' AND Platform = 'linux';
-- Returns 1890599

因此,与“linux”相关的记录比“windows”多 600K。

但是,这个查询会在 6-9 秒内返回...

SELECT MAX(id) FROM TableName WHERE Category = 'session' AND Platform = 'linux';

然而,这个我必须在等待超过 13 分钟后才能获得结果......

SELECT MAX(id) FROM TableName WHERE Category = 'session' AND Platform = 'windows';

哦...我桌子上还有以下索引...

CREATE NONCLUSTERED INDEX [IX_TableName_CategoryPlatform] ON [dbo].[TableName]
(
    [Platform] ASC,
    [Category] ASC,
    [CreateDate] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO

威士忌、探戈、狐步舞?

为什么搜索词会产生影响,尤其是在有索引的情况下?

更新

我刚刚做了以下观察...

SELECT MAX(id) FROM TableName WHERE Platform = 'windows';

通过从查询中删除 Category,可以非常快速地返回响应...

更新 2

我已按要求创建了几个执行计划。然而,我注意到的是,“粘贴计划”实用程序生成的计划中的百分比与我在 SSMS 中得到的百分比似乎不同,所以我在每个链接下方包括了我在管理中看到的百分比工作室。

对于以下查询(有效)...

SELECT MAX([MessageID]) [MaxID] FROM [BoothComm].[UniversalMessageQueue] WHERE [MessagePlatform]='windows';

https://www.brentozar.com/pastetheplan/?id=Sk9q59CqZ

0%:选择 0%:流聚合 0%:顶部 100%:索引扫描

下一个查询(不起作用)我只能提供一个ESTIMATED执行计划。

SELECT 
   MAX(MessageID) AS [MaxID]
FROM BoothComm.UniversalMessageQueue
WHERE 
 MessageCategory = 'session'
 AND
 MessagePlatform = 'windows'

https://www.brentozar.com/pastetheplan/?id=r1zqnq09-

0%:选择 0%:流聚合 0%:顶部 0% : 嵌套循环(内连接) -- 为什么会出现这种情况?? 21%:索引扫描 79% : Key Lookup -- 也是新的,似乎比其他任何事情都要占用更多的时间

(感谢大家的帮助!)

更新 3

所以在进行了以下所有对话和更改之后,我仍然有一个问题......

为什么这个查询会在 1 秒内返回(感谢将 ID 添加到索引中)...

SELECT 
      MAX(MessageID) AS [MaxID]
FROM BoothComm.UniversalMessageQueue
WHERE 
    MessagePlatform = 'linux'
    AND
    MessageCategory = 'accounting'

而这个运行需要 13 -22 秒...

SELECT 
      MAX(MessageID) AS [MaxID]
FROM BoothComm.UniversalMessageQueue
WHERE 
    MessagePlatform = 'windows'
    AND
    MessageCategory = 'accounting'

同一张表,同样的索引,执行计划是完全一样的。除了 MessagePlatform 值之外,一切都是相同的。并且导致延迟的值出现在比另一个更少的记录上。

【问题讨论】:

id 你的集群密钥吗? 执行计划显示什么以及它是否使用您的索引? 如果将ID 作为INCLUDE 添加到索引中怎么样? 使用Paste The Plan @ brentozar.com 分享您的执行计划,以下是说明:How to Use Paste the Plan。 另外,分享您的表架构。 【参考方案1】:

您的查询速度很慢,因为该表未标准化。您不应该将类别和平台作为字符串存储在每条记录上。相反,它们应该在具有整数主键的查找表中。然后,这些键将存储在您的主表中,每个键上都有适当的非聚集索引。然后你应该在你的主表中添加一个聚集索引到一个有意义的按升序排序的列(最好是一个唯一的整数)。

至于您遇到的实际问题,如果您没有定义聚集索引,则数据存储在堆中(即未排序的数据堆)。您拥有的索引将有所帮助,但性能受到您使用字符串作为键的事实的阻碍,并且从它的外观来看,这些字符串不是高度特定的(许多重复)。 SQL Server 可能只是决定进行全面扫描来回答您的问题,因为它估计这将比任何其他方法更快。

【讨论】:

虽然我承认并同意您所描述的规范化做法,但此表包含及时的“快照”。问题是可以更改引用的值,但不能更改这些记录。因此,虽然 5 可能比“windows”更有效,但 5 可能并不总是指“windows”,但该记录必须始终反映“windows”。 另外,我确实定义了一个聚集索引(主键)。它只是不涉及此查询。鉴于一张表只允许有一个聚集索引,非聚集索引是唯一剩下的选择。 您可以处理查找表中的更改历史记录。创建新查找以向系统添加新值。确保添加日期戳以反映更改发生的时间。至于聚集索引,它们在幕后的每个查询中都会用到。每个非聚集索引存储聚集键以指向原始记录。如果您显示表定义会有所帮助。

以上是关于SQL Server 中的查询性能的主要内容,如果未能解决你的问题,请参考以下文章

CTE 中的 SQL Server 视图导致性能不佳

如何提高 SQL Server 查询的性能以选择具有值的行不在子查询中的一次计数

我可以通过在 EF Core 和 SQL Server 中的 OrderBy() 之前使用 Where() 来提高查询的性能吗?

SQL Server 中的 SQL 查询优化

SQL Server 性能查询

SQL Server 中的视图