ksp*_*rin 9 performance sql-server execution-plan azure-sql-database cpu query-performance
我有一个支持 .NET Core API 应用程序的 Azure SQL 数据库。浏览 Azure 门户中的性能概览报告表明,我的数据库服务器上的大部分负载(DTU 使用情况)来自 CPU,特别是一个查询:
正如我们所见,查询 3780 负责几乎所有服务器上的 CPU 使用率。
这在某种程度上是有道理的,因为查询 3780(见下文)基本上是整个应用程序的关键,并且经常被用户调用。这也是一个相当复杂的查询,需要许多连接才能获得所需的正确数据集。查询来自一个最终看起来像这样的 sproc:
-- @UserId UNIQUEIDENTIFIER
SELECT
C.[Id],
C.[UserId],
C.[OrganizationId],
C.[Type],
C.[Data],
C.[Attachments],
C.[CreationDate],
C.[RevisionDate],
CASE
WHEN
@UserId IS NULL
OR C.[Favorites] IS NULL
OR JSON_VALUE(C.[Favorites], CONCAT('$."', @UserId, '"')) IS NULL
THEN 0
ELSE 1
END [Favorite],
CASE
WHEN
@UserId IS NULL
OR C.[Folders] IS NULL
THEN NULL
ELSE TRY_CONVERT(UNIQUEIDENTIFIER, JSON_VALUE(C.[Folders], CONCAT('$."', @UserId, '"')))
END [FolderId],
CASE
WHEN C.[UserId] IS NOT NULL OR OU.[AccessAll] = 1 OR CU.[ReadOnly] = 0 OR G.[AccessAll] = 1 OR CG.[ReadOnly] = 0 THEN 1
ELSE 0
END [Edit],
CASE
WHEN C.[UserId] IS NULL AND O.[UseTotp] = 1 THEN 1
ELSE 0
END [OrganizationUseTotp]
FROM
[dbo].[Cipher] C
LEFT JOIN
[dbo].[Organization] O ON C.[UserId] IS NULL AND O.[Id] = C.[OrganizationId]
LEFT JOIN
[dbo].[OrganizationUser] OU ON OU.[OrganizationId] = O.[Id] AND OU.[UserId] = @UserId
LEFT JOIN
[dbo].[CollectionCipher] CC ON C.[UserId] IS NULL AND OU.[AccessAll] = 0 AND CC.[CipherId] = C.[Id]
LEFT JOIN
[dbo].[CollectionUser] CU ON CU.[CollectionId] = CC.[CollectionId] AND CU.[OrganizationUserId] = OU.[Id]
LEFT JOIN
[dbo].[GroupUser] GU ON C.[UserId] IS NULL AND CU.[CollectionId] IS NULL AND OU.[AccessAll] = 0 AND GU.[OrganizationUserId] = OU.[Id]
LEFT JOIN
[dbo].[Group] G ON G.[Id] = GU.[GroupId]
LEFT JOIN
[dbo].[CollectionGroup] CG ON G.[AccessAll] = 0 AND CG.[CollectionId] = CC.[CollectionId] AND CG.[GroupId] = GU.[GroupId]
WHERE
C.[UserId] = @UserId
OR (
C.[UserId] IS NULL
AND OU.[Status] = 2
AND O.[Enabled] = 1
AND (
OU.[AccessAll] = 1
OR CU.[CollectionId] IS NOT NULL
OR G.[AccessAll] = 1
OR CG.[CollectionId] IS NOT NULL
)
)
Run Code Online (Sandbox Code Playgroud)
如果您关心,可以在此处的 GitHub 上找到此数据库的完整源代码。来自上述查询的来源:
几个月来,我花了一些时间在这个查询上,尽我所知调整执行计划,最终得到它的当前状态。使用此执行计划的查询在数百万行(< 1 秒)中速度很快,但如上所述,随着应用程序大小的增长,服务器 CPU 的消耗越来越大。
我在下面附上了实际的查询计划(不确定在堆栈交换中是否有任何其他方式可以在此处共享该计划),它显示了在生产中针对大约 400 个结果的返回数据集执行 sproc。
我正在寻求澄清的一些要点:
Index Seek on[IX_Cipher_UserId_Type_IncludeAll]
占计划总成本的 57%。我对计划的理解是这个成本与IO有关,这使得Cipher表包含数百万条记录。但是,Azure SQL 性能报告显示我的问题源于此查询的 CPU,而不是 IO,所以我不确定这是否真的是一个问题。此外,它已经在这里进行了索引查找,所以我不确定是否有任何改进的余地。
来自所有连接的哈希匹配操作似乎表明计划中 CPU 使用率很高(我认为?),但我不确定如何做得更好。我需要如何获取数据的复杂性质需要跨多个表进行大量连接。如果可能,我已经在它们的ON
子句中短路了其中的许多连接(基于先前连接的结果)。
在这里下载完整的执行计划:https : //www.dropbox.com/s/lua1awsc0uz1lo9/CipherDetails_ReadByUserId.sqlplan?dl=0
我觉得我可以从这个查询中获得更好的 CPU 性能,但我处于一个阶段,我不确定如何进一步调整执行计划。还可以进行哪些其他优化来降低 CPU 负载?执行计划中的哪些操作对 CPU 使用率影响最大?
您可以在 SQL Server Management Studio 中查看操作员级别的 CPU 和运行时间指标,尽管我不能说它们对于像您一样快速完成的查询来说有多可靠。您的计划仅具有行模式运算符,因此时间指标适用于该运算符及其下方子树中的运算符。以嵌套循环连接为例,SQL Server 告诉您整个子树花费了 60 毫秒的 CPU 时间和 80 毫秒的运行时间:
该子树的大部分时间都花在索引查找上。索引查找也占用CPU。看起来您的索引恰好具有所需的列,因此不清楚如何降低该运算符的 CPU 成本。除了查找之外,计划中的大部分 CPU 时间都花在实现连接的哈希匹配上。
这是一个巨大的过度简化,但这些散列连接占用的 CPU 将取决于散列表输入的大小以及探测端处理的行数。观察有关此查询计划的一些事情:
C.[UserId] = @UserId
. 这些行根本不关心连接。OU.[UserId] = @UserId
)。[vault].[dbo].[Cipher].[UserId] as [C].[UserId]=[@UserId] OR ([vault].[dbo].[OrganizationUser].[AccessAll] as [OU].[AccessAll]=(1) OR [vault].[dbo].[CollectionUser].[CollectionId] as [CU].[CollectionId] IS NOT NULL OR [vault].[dbo].[Group].[AccessAll] as [G].[AccessAll]=(1) OR [vault].[dbo].[CollectionGroup].[CollectionId] as [CG].[CollectionId] IS NOT NULL) AND [vault].[dbo].[Cipher].[UserId] as [C].[UserId] IS NULL AND [vault].[dbo].[OrganizationUser].[Status] as [OU].[Status]=(2) AND [vault].[dbo].[Organization].[Enabled] as [O].[Enabled]=(1)
将您的查询编写为UNION ALL
. 前半部分UNION ALL
可以包括行 where C.[UserId] = @UserId
,后半部分可以包括行 where C.[UserId] IS NULL
。您已经在进行两次索引搜索[dbo].[Cipher]
(一次针对 NULL @UserId
,一次针对 NULL),因此该UNION ALL
版本似乎不太可能会变慢。单独写出查询将允许您在构建和探测方面尽早进行一些过滤。如果需要处理较少的中间数据,查询会更快。
我不知道您的 SQL Server 版本是否支持此功能,但如果这没有帮助,您可以尝试在查询中添加列存储索引,以使散列连接符合批处理模式的条件。我首选的方法是创建一个带有 CCI 的空表,并左连接到该表。与行模式相比,散列连接在批处理模式下运行时效率更高。
归档时间: |
|
查看次数: |
6637 次 |
最近记录: |