如何诊断slow/inconsistentSQL服务器查询?
How to diagnose slow/inconsistent SQL Server query?
运行 Windows Server 2012,Hyper-V,SQL Server 2012 Active/Passive 故障转移集群 w/two 8 处理器,60GB 节点,单实例, 300 个数据库。此查询产生不一致的结果,运行 介于 10 到 30 秒之间。
DECLARE @OrgID BigInt = 780246
DECLARE @ActiveOnly Bit = 0
DECLARE @RestrictToOrgID Bit = 0;
WITH og (OrgID, GroupID) AS
(
SELECT ID, ID FROM Common.com.Organizations WHERE ISNULL(ParentID, 0) <> ID
UNION ALL
SELECT o.ID, og.GroupID FROM Common.com.Organizations o JOIN og ON og.OrgID = o.ParentID
)
SELECT e.*, v.Type AS VendorType, v.F1099, v.F1099Type, v.TaxID, v.TaxPercent,
v.ContactName, v.ContactPhone, v.ContactEMail, v.DistrictWide,
a.*
FROM og
JOIN books.Organizations bo ON bo.CommonID = og.OrgID
JOIN books.Organizations po ON po.CommonID = og.GroupID
JOIN books.Entities e ON e.OrgID = po.ID
JOIN Vendors v ON v.ID = e.ID
AND (e.OrgID = bo.ID OR v.DistrictWide = 1)
LEFT JOIN Addresses a ON a.ID = e.AddressID
WHERE bo.ID = @OrgID
AND (@ActiveOnly = 0 OR e.Active = 1)
AND (@RestrictToOrgID = 0 OR e.OrgID = @OrgID)
ORDER BY e.EntityName
将 LEFT JOIN Addresses
替换为 JOIN Addresses
JOIN Addresses a ON a.ID = e.AddressID
WHERE bo.ID = @OrgID
AND (@ActiveOnly = 0 OR e.Active = 1)
AND (@RestrictToOrgID = 0 OR e.OrgID = @OrgID)
ORDER BY e.EntityName
或将从 Addresses
中选择的列的长度减少到小于 100 字节
SELECT e.*, v.Type AS VendorType, v.F1099, v.F1099Type, v.TaxID, v.TaxPercent,
v.ContactName, v.ContactPhone, v.ContactEMail, v.DistrictWide,
a.Fax
将执行时间减少到大约 0.5 秒。
此外,使用 SELECT DISTINCT
并将 books.Entities
连接到 Vendors
SELECT DISTINCT e.*, v.Type AS VendorType, v.F1099, v.F1099Type, v.TaxID, v.TaxPercent,
v.ContactName, v.ContactPhone, v.ContactEMail, v.DistrictWide,
a.*
FROM og
JOIN books.Organizations bo ON bo.CommonID = og.OrgID
JOIN books.Organizations po ON po.CommonID = og.GroupID
JOIN Vendors v
JOIN books.Entities e ON v.ID = e.ID
ON e.OrgID = bo.ID OR (e.OrgID = po.ID AND v.DistrictWide = 1)
将时间减少到大约 0.75 秒。
总结
这些情况表明 SQL 服务器实例中存在某种资源限制,导致这些不稳定的结果,我不知道如何诊断它。如果我将有问题的数据库复制到我的笔记本电脑 运行 SQL Server 2012,问题就不会出现。我可以继续改变 SQL 并希望得到最好的结果,但我更愿意找到一个更明确的解决方案。
如有任何建议,我们将不胜感激。
更新 2/27/18
未修改查询的执行计划显示针对地址 table 的聚簇索引查找是问题所在。
将从 Addresses
中选择的列的长度减少到小于 100 字节
SELECT e.*, v.Type AS VendorType, v.F1099, v.F1099Type, v.TaxID, v.TaxPercent,
v.ContactName, v.ContactPhone, v.ContactEMail, v.DistrictWide,
a.Fax
用聚簇索引扫描替换聚簇索引查找以检索 a.Fax
并用哈希匹配将此值连接到结果。
Addresses
table 主键创建如下:
ALTER TABLE dbo.Addresses
ADD CONSTRAINT PK_Addresses 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)
ON PRIMARY
该索引每天都会根据需要进行碎片整理和优化。
到目前为止,我找不到任何关于为什么 Clustered Index Seek 会增加查询时间的有用信息。
好的,通常情况下,不是一个问题,而是两个问题。这是复杂问题分析可能导致错误结论的示例。
原来的主要问题是递归 CTE og
,其中 returns 一个枢轴 table 给出了组织之间的 parent/child 关系。然而,对执行计划的分析似乎表明问题出在优化器中与从左连接 table 返回的数据量有关的某种故障。这可能完全是我无法正确分析执行计划的结果,但 SQL Server 2012 SP4 在这些情况下如何创建执行计划似乎确实存在一些问题。
虽然在我们的生产服务器上更为重要,但 SQL 服务器对递归 CTE 的优化问题在我的本地主机 运行 2012 SP4 和登台服务器 运行 SP2。但需要进一步分析和一些猜测才能看到它。
解决方案
我用一个枢轴 table 替换了递归 CTE,并向组织 table 添加了一个触发器来维护它。
USE Common
GO
CREATE VIEW com.OrganizationGroupsCTE
AS
WITH cte (OrgID, GroupID) AS
(
SELECT ID, ID FROM com.Organizations WHERE ISNULL(ParentID, 0) <> ID
UNION ALL
SELECT o.ID, cte.GroupID FROM com.Organizations o JOIN cte ON cte.OrgID = o.ParentID
)
SELECT OrgID, GroupID FROM cte
GO
CREATE TABLE com.OrganizationGroups
(
OrgID BIGINT,
GroupID BIGINT
)
INSERT com.OrganizationGroups
SELECT OrgID, GroupID
FROM com.OrganizationGroupsCTE
GO
CREATE TRIGGER TR_OrganizationGroups ON com.Organizations AFTER INSERT,UPDATE,DELETE
AS
DELETE og
FROM com.OrganizationGroups og
JOIN deleted d ON d.ID IN (og.groupID, og.orgID);
INSERT com.OrganizationGroups
SELECT orgID, groupID
FROM inserted i
JOIN OrganizationGroupsCTE cte ON i.ID IN (cte.orgID, cte.groupID)
GO
修改查询以使用主元后 table,
SELECT e.*, v.Type AS VendorType, v.F1099, v.F1099Type, v.TaxID, v.TaxPercent,
v.ContactName, v.ContactPhone, v.ContactEMail, v.DistrictWide,
a.*
FROM Common.com.OrganizationGroups og
JOIN books.Organizations bo ON bo.CommonID = og.OrgID
JOIN books.Organizations po ON po.CommonID = og.GroupID
JOIN books.Entities e ON e.OrgID = po.ID
JOIN Vendors v ON v.ID = e.ID
AND (e.OrgID = bo.ID OR v.DistrictWide = 1)
LEFT JOIN Addresses a ON a.ID = e.AddressID
WHERE bo.ID = @OrgID
AND (@ActiveOnly = 0 OR e.Active = 1)
AND (@RestrictToOrgID = 0 OR e.OrgID = @OrgID)
ORDER BY e.EntityName
SQL 服务器性能在所有三种环境中都得到了改进,并且保持一致。生产服务器上的问题现已消除。
运行 Windows Server 2012,Hyper-V,SQL Server 2012 Active/Passive 故障转移集群 w/two 8 处理器,60GB 节点,单实例, 300 个数据库。此查询产生不一致的结果,运行 介于 10 到 30 秒之间。
DECLARE @OrgID BigInt = 780246
DECLARE @ActiveOnly Bit = 0
DECLARE @RestrictToOrgID Bit = 0;
WITH og (OrgID, GroupID) AS
(
SELECT ID, ID FROM Common.com.Organizations WHERE ISNULL(ParentID, 0) <> ID
UNION ALL
SELECT o.ID, og.GroupID FROM Common.com.Organizations o JOIN og ON og.OrgID = o.ParentID
)
SELECT e.*, v.Type AS VendorType, v.F1099, v.F1099Type, v.TaxID, v.TaxPercent,
v.ContactName, v.ContactPhone, v.ContactEMail, v.DistrictWide,
a.*
FROM og
JOIN books.Organizations bo ON bo.CommonID = og.OrgID
JOIN books.Organizations po ON po.CommonID = og.GroupID
JOIN books.Entities e ON e.OrgID = po.ID
JOIN Vendors v ON v.ID = e.ID
AND (e.OrgID = bo.ID OR v.DistrictWide = 1)
LEFT JOIN Addresses a ON a.ID = e.AddressID
WHERE bo.ID = @OrgID
AND (@ActiveOnly = 0 OR e.Active = 1)
AND (@RestrictToOrgID = 0 OR e.OrgID = @OrgID)
ORDER BY e.EntityName
将 LEFT JOIN Addresses
替换为 JOIN Addresses
JOIN Addresses a ON a.ID = e.AddressID
WHERE bo.ID = @OrgID
AND (@ActiveOnly = 0 OR e.Active = 1)
AND (@RestrictToOrgID = 0 OR e.OrgID = @OrgID)
ORDER BY e.EntityName
或将从 Addresses
中选择的列的长度减少到小于 100 字节
SELECT e.*, v.Type AS VendorType, v.F1099, v.F1099Type, v.TaxID, v.TaxPercent,
v.ContactName, v.ContactPhone, v.ContactEMail, v.DistrictWide,
a.Fax
将执行时间减少到大约 0.5 秒。
此外,使用 SELECT DISTINCT
并将 books.Entities
连接到 Vendors
SELECT DISTINCT e.*, v.Type AS VendorType, v.F1099, v.F1099Type, v.TaxID, v.TaxPercent,
v.ContactName, v.ContactPhone, v.ContactEMail, v.DistrictWide,
a.*
FROM og
JOIN books.Organizations bo ON bo.CommonID = og.OrgID
JOIN books.Organizations po ON po.CommonID = og.GroupID
JOIN Vendors v
JOIN books.Entities e ON v.ID = e.ID
ON e.OrgID = bo.ID OR (e.OrgID = po.ID AND v.DistrictWide = 1)
将时间减少到大约 0.75 秒。
总结
这些情况表明 SQL 服务器实例中存在某种资源限制,导致这些不稳定的结果,我不知道如何诊断它。如果我将有问题的数据库复制到我的笔记本电脑 运行 SQL Server 2012,问题就不会出现。我可以继续改变 SQL 并希望得到最好的结果,但我更愿意找到一个更明确的解决方案。
如有任何建议,我们将不胜感激。
更新 2/27/18
未修改查询的执行计划显示针对地址 table 的聚簇索引查找是问题所在。
将从 Addresses
中选择的列的长度减少到小于 100 字节
SELECT e.*, v.Type AS VendorType, v.F1099, v.F1099Type, v.TaxID, v.TaxPercent,
v.ContactName, v.ContactPhone, v.ContactEMail, v.DistrictWide,
a.Fax
用聚簇索引扫描替换聚簇索引查找以检索 a.Fax
并用哈希匹配将此值连接到结果。
Addresses
table 主键创建如下:
ALTER TABLE dbo.Addresses
ADD CONSTRAINT PK_Addresses 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)
ON PRIMARY
该索引每天都会根据需要进行碎片整理和优化。
到目前为止,我找不到任何关于为什么 Clustered Index Seek 会增加查询时间的有用信息。
好的,通常情况下,不是一个问题,而是两个问题。这是复杂问题分析可能导致错误结论的示例。
原来的主要问题是递归 CTE og
,其中 returns 一个枢轴 table 给出了组织之间的 parent/child 关系。然而,对执行计划的分析似乎表明问题出在优化器中与从左连接 table 返回的数据量有关的某种故障。这可能完全是我无法正确分析执行计划的结果,但 SQL Server 2012 SP4 在这些情况下如何创建执行计划似乎确实存在一些问题。
虽然在我们的生产服务器上更为重要,但 SQL 服务器对递归 CTE 的优化问题在我的本地主机 运行 2012 SP4 和登台服务器 运行 SP2。但需要进一步分析和一些猜测才能看到它。
解决方案
我用一个枢轴 table 替换了递归 CTE,并向组织 table 添加了一个触发器来维护它。
USE Common
GO
CREATE VIEW com.OrganizationGroupsCTE
AS
WITH cte (OrgID, GroupID) AS
(
SELECT ID, ID FROM com.Organizations WHERE ISNULL(ParentID, 0) <> ID
UNION ALL
SELECT o.ID, cte.GroupID FROM com.Organizations o JOIN cte ON cte.OrgID = o.ParentID
)
SELECT OrgID, GroupID FROM cte
GO
CREATE TABLE com.OrganizationGroups
(
OrgID BIGINT,
GroupID BIGINT
)
INSERT com.OrganizationGroups
SELECT OrgID, GroupID
FROM com.OrganizationGroupsCTE
GO
CREATE TRIGGER TR_OrganizationGroups ON com.Organizations AFTER INSERT,UPDATE,DELETE
AS
DELETE og
FROM com.OrganizationGroups og
JOIN deleted d ON d.ID IN (og.groupID, og.orgID);
INSERT com.OrganizationGroups
SELECT orgID, groupID
FROM inserted i
JOIN OrganizationGroupsCTE cte ON i.ID IN (cte.orgID, cte.groupID)
GO
修改查询以使用主元后 table,
SELECT e.*, v.Type AS VendorType, v.F1099, v.F1099Type, v.TaxID, v.TaxPercent,
v.ContactName, v.ContactPhone, v.ContactEMail, v.DistrictWide,
a.*
FROM Common.com.OrganizationGroups og
JOIN books.Organizations bo ON bo.CommonID = og.OrgID
JOIN books.Organizations po ON po.CommonID = og.GroupID
JOIN books.Entities e ON e.OrgID = po.ID
JOIN Vendors v ON v.ID = e.ID
AND (e.OrgID = bo.ID OR v.DistrictWide = 1)
LEFT JOIN Addresses a ON a.ID = e.AddressID
WHERE bo.ID = @OrgID
AND (@ActiveOnly = 0 OR e.Active = 1)
AND (@RestrictToOrgID = 0 OR e.OrgID = @OrgID)
ORDER BY e.EntityName
SQL 服务器性能在所有三种环境中都得到了改进,并且保持一致。生产服务器上的问题现已消除。