我有一个视图,它可以快速运行(几秒钟)最多 41 条记录(例如TOP 41),但需要几分钟才能运行 44 条或更多条记录,如果使用TOP 42或运行会产生中间结果TOP 43。具体来说,它将在几秒钟内返回前 39 条记录,然后在返回剩余记录之前暂停近三分钟。这个模式在查询TOP 44or时是一样的TOP 100。
这个视图最初是从一个基本视图派生出来的,在基本视图中只添加了一个过滤器,下面代码中的最后一个。如果我从基础链接子视图,或者如果我使用内联基础中的代码编写子视图,似乎没有区别。基本视图在几秒钟内返回 100 条记录。我想我可以让子视图以与基础一样快的速度运行,而不是慢 50 倍。有没有人见过这种行为?关于原因或解决方案的任何猜测?
这种行为在过去几个小时内一直是一致的,因为我已经测试了所涉及的查询,尽管在事情开始变慢之前返回的行数略有上升和下降。这并不新鲜;我现在正在查看它,因为总运行时间是可以接受的(<2 分钟),但我已经在相关的日志文件中看到这种暂停至少几个月了。
我从未见过查询被阻止,即使数据库上没有其他活动(由 sp_WhoIsActive 验证),问题仍然存在。基本视图包括NOLOCK整个内容,这是值得的。
这是子视图的简化版本,为简单起见,基本视图内联。它仍然表现出运行时间的跳跃,大约有 40 条记录。
SELECT TOP 100 PERCENT
Map.SalesforceAccountID AS Id,
CAST(C.CustomerID AS NVARCHAR(255)) AS Name,
CASE WHEN C.StreetAddress = 'Unknown' THEN '' ELSE C.StreetAddress END AS BillingStreet,
CASE WHEN C.City = 'Unknown' THEN '' ELSE SUBSTRING(C.City, 1, 40) END AS BillingCity,
SUBSTRING(C.Region, 1, 20) AS BillingState,
CASE WHEN C.PostalCode = 'Unknown' THEN '' ELSE SUBSTRING(C.PostalCode, 1, 20) END AS BillingPostalCode,
CASE WHEN C.Country = 'Unknown' THEN '' ELSE SUBSTRING(C.Country, 1, 40) END AS BillingCountry,
CASE WHEN C.PhoneNumber = 'Unknown' THEN '' ELSE C.PhoneNumber END AS Phone,
CASE WHEN C.FaxNumber = 'Unknown' THEN '' ELSE C.FaxNumber END AS Fax,
TransC.WebsiteAddress AS Website,
C.AccessKey AS AccessKey__c,
CASE WHEN dbo.ValidateEMail(C.EMailAddress) = 1 THEN C.EMailAddress END, -- Removing this UDF does not speed things
TransC.EmailSubscriber
-- A couple dozen additional TransC fields
FROM
WarehouseCustomers AS C WITH (NOLOCK)
INNER JOIN TransactionalCustomers AS TransC WITH (NOLOCK) ON C.CustomerID = TransC.CustomerID
LEFT JOIN Salesforce.AccountsMap AS Map WITH (NOLOCK) ON C.CustomerID = Map.CustomerID
WHERE
C.DateMadeObsolete IS NULL
AND C.EmailAddress NOT LIKE '%@volusion.%'
AND C.AccessKey IN ('C', 'R')
AND C.CustomerID NOT IN (243566) -- Exclude specific test records
AND EXISTS (SELECT * FROM Orders AS O WHERE C.CustomerID = O.CustomerID AND O.OrderDate >= '2010-06-28') -- Only count customers who've placed a recent order
AND Map.SalesforceAccountID IS NULL -- Only count customers not already uploaded to Salesforce
-- Removing the ORDER BY clause does not speed things up
ORDER BY
C.CustomerID DESC
Run Code Online (Sandbox Code Playgroud)
该Id IS NULL过滤器会丢弃由BaseView;返回的大部分记录。如果没有TOP子句,它们分别返回 1,100 条记录和 267K。
运行时TOP 40:
SQL Server parse and compile time: CPU time = 234 ms, elapsed time = 247 ms.
SQL Server Execution Times: CPU time = 0 ms, elapsed time = 0 ms.
SQL Server Execution Times: CPU time = 0 ms, elapsed time = 0 ms.
(40 row(s) affected)
Table 'CustomersHistory'. Scan count 2, logical reads 39112, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Orders'. Scan count 1, logical reads 752, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'AccountsMap'. Scan count 1, logical reads 458, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times: CPU time = 2199 ms, elapsed time = 7644 ms.
Run Code Online (Sandbox Code Playgroud)
运行时TOP 45:
(45 row(s) affected)
Table 'CustomersHistory'. Scan count 2, logical reads 98268, physical reads 1, read-ahead reads 3, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Orders'. Scan count 1, logical reads 1788, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'AccountsMap'. Scan count 1, logical reads 2152, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times: CPU time = 41980 ms, elapsed time = 177231 ms.
Run Code Online (Sandbox Code Playgroud)
我很惊讶地看到实际输出的这种适度差异导致读取次数增加了 ~3 倍。
比较执行计划,除了返回的行数外,它们是相同的。与上面的统计数据一样,TOP 45查询中早期步骤的实际行数要高得多,而不仅仅是高 12.5%。
概括地说,它是从 Orders 中扫描一个覆盖索引,从 WarehouseCustomers 中寻找相应的记录;将此循环加入到 TransactionalCustomers(远程查询,确切计划未知);并将其与 AccountsMap 的表扫描合并。远程查询是估计成本的 94%。
早些时候,当我将视图的扩展内容作为独立查询执行时,它运行得非常快:100 条记录需要 13 秒。我现在正在测试一个精简版的查询,没有子查询,这个简单得多的查询需要三分钟才能要求返回超过 40 行,即使作为独立查询运行也是如此。
子视图包含大量读取(每个 sp_WhoIsActive 约 1M),但在这台机器上(8 个内核,32 GB RAM,95% 专用 SQL 盒),这通常不是问题。
我已经多次删除并重新创建了这两个视图,没有任何变化。
数据不包括任何 TEXT 或 BLOB 字段。一个字段涉及 UDF;删除它不会阻止暂停。
无论是在服务器上查询还是在 1,400 英里外的工作站上查询,时间都是相似的,因此延迟似乎是查询本身固有的,而不是将结果发送到客户端。
一些值得尝试的事情:
检查你的索引
所有JOIN关键字段都已索引吗?如果您经常使用此视图,我什至会为视图中的条件添加过滤索引。例如...
CREATE INDEX ix_CustomerId ON WarehouseCustomers(CustomerId, EmailAddress)
WHERE
DateMadeObsolete IS NULL
AND AccessKey IN ('C', 'R')
AND CustomerID NOT IN (243566)
更新统计数据
FULLSCAN。如果存在大量行,则数据可能已发生显着更改而不会触发自动重新计算。清理查询
制作Map JOINa NOT EXISTS- 您不需要该表中的任何数据,因为您只需要不匹配的记录
去除ORDER BY。我知道评论说这并不重要,但我发现这很难相信。对于较小的结果集来说,这可能并不重要,因为数据页已经被缓存。
| 归档时间: |
|
| 查看次数: |
2405 次 |
| 最近记录: |