我们已经将数据库托管在Azure中,并且正在此数据库上运行存储过程。直到上周,存储过程一直运行良好,但突然开始提供错误的连接超时。
我们的数据库大小为 14 GB ,存储过程通常返回 2k至20k 记录,并且我们使用的是 S3定价层(50 DTU)< / b> Azure数据库。
我发现有趣的是第一次执行存储过程,这需要花费大量时间2-3分钟,这导致了超时。以后的执行速度很快(也许会缓存执行计划)。
另外,当我在配置为8gb ram的计算机上以相同记录数在同一数据库上运行时,Win10在 15秒内运行。
这是我的存储过程:
CREATE PROCEDURE [dbo].[PRSP]
@CompanyID INT,
@fromDate DATETIME,
@toDate DATETIME,
@ListMailboxId as MailboxIds Readonly,
@ListConversationType as ConversationTypes Readonly
AS
BEGIN
SET NOCOUNT ON;
SELECT
C.ID,
C.MailboxID,
C.Status,
C.CustomerID,
Cust.FName,
Cust.LName,
C.ArrivalDate as ConversationArrivalDate,
C.[ClosureDate],
C.[ConversationType],
M.[From],
M.ArrivalDate as MessageArrivalDate,
M.ID as MessageID
FROM
[Conversation] as C
INNER JOIN
[ConversationHistory] AS CHis ON (CHis.ConversationID = C.ID)
INNER JOIN
[Message] AS M ON (M.ConversationID = C.ID)
INNER JOIN
[Mailbox] AS Mb ON (Mb.ID = C.MailboxID)
INNER JOIN
[Customer] AS Cust ON (Cust.ID = C.CustomerID)
JOIN
@ListConversationType AS convType ON convType.ID = C.[ConversationType]
JOIN
@ListMailboxId AS mailboxIds ON mailboxIds.ID = Mb.ID
WHERE
Mb.CompanyID = @CompanyID
AND ((CHis.CreatedOn > @fromDate
AND CHis.CreatedOn < @toDate
AND CHis.Activity = 1
AND CHis.TagData = '3')
OR (M.ArrivalDate > @fromDate
AND M.ArrivalDate < @toDate))
END
这是执行计划:
请提出您需要改进的建议?我们还需要升级我的价格等级吗?
理想情况下,对于14GB数据库,Azure定价层应该是什么?
答案 0 :(得分:1)
在Windows 10 8Gb RAM计算机上,该查询需要1到3秒才能完成。因为SQL Server选择了较差的执行计划,所以需要15秒。在这种情况下,执行计划不佳的根本原因是估计错误,计划中的多个运算符显示了估计行与实际行之间的巨大差异。例如,SQL Server估计它只需要对pk_customer聚集索引执行一次查找,但是它执行了16,522次查找。 [ConversationHistory]。[IX_ConversationID_CreatedOn_Activity_ByWhom]和[Message]。[IX_ConversationID_ID_ID_ArrivalDt_From_RStatus_Type。
以下是一些提示,您可以按照这些提示来提高查询的性能:
OPTION (HASH JOIN)
。可能会改善
性能,否则可能会降低速度,甚至可能导致查询
错误。 SELECT * INTO #temp_table FROM @table_variable
)。表变量没有统计信息,导致估计错误。SELECT * INTO #operator_result FROM (query equivalent to operator)
。 Query2:使用#operator_result
编写查询。由于#operator_result
是时间表,因此SQL Server被迫重新评估估计值。在这种情况下,令人讨厌的运算符是哈希匹配(内部联接)您还可以采取其他措施来提高此查询的性能:
Conversation.PK_dbo.Conversation
聚集索引中有16,522个键查找。通过创建适当的覆盖索引可以避免这种情况。在这种情况下,覆盖指数如下: DROP INDEX [IX_MailboxID] ON [dbo].[Conversation]
GO
CREATE INDEX IX_MailboxID ON [dbo].[Conversation](MailboxID)
INCLUDE (ArrivalDate, Status, ClosureDate, CustomerID, ConversationType)
UNION
或UNION ALL
。例如:代替:
SELECT *
FROM table
WHERE <predicate1> OR <predicate2>
使用:
SELECT *
FROM table
WHERE <predicate1>
UNION
SELECT *
FROM table
WHERE <predicate2>
有时可以提高性能。
分别应用每个提示并衡量效果。
编辑:您可以尝试以下方法,看看它是否可以提高性能:
SELECT
C.ID,
C.MailboxID,
C.Status,
C.CustomerID,
Cust.FName,
Cust.LName,
C.ArrivalDate as ConversationArrivalDate,
C.[ClosureDate],
C.[ConversationType],
M.[From],
M.ArrivalDate as MessageArrivalDate,
M.ID as MessageID
FROM
@ListConversationType AS convType
INNER JOIN (
@ListMailboxId AS mailboxIds
INNER JOIN
[Mailbox] AS Mb ON (Mb.ID = mailboxIds.MailboxID)
INNER JOIN
[Conversation] as C
ON C.ID = Mb.ID
) ON convType.ID = C.[ConversationType]
INNER HASH JOIN
[Customer] AS Cust ON (Cust.ID = C.CustomerID)
INNER HASH JOIN
[ConversationHistory] AS CHis ON (CHis.ConversationID = C.ID)
INNER HASH JOIN
[Message] AS M ON (M.ConversationID = C.ID)
WHERE
Mb.CompanyID = @CompanyID
AND ((CHis.CreatedOn > @fromDate
AND CHis.CreatedOn < @toDate
AND CHis.Activity = 1
AND CHis.TagData = '3')
OR (M.ArrivalDate > @fromDate
AND M.ArrivalDate < @toDate))
这:
SELECT
C.ID,
C.MailboxID,
C.Status,
C.CustomerID,
Cust.FName,
Cust.LName,
C.ArrivalDate as ConversationArrivalDate,
C.[ClosureDate],
C.[ConversationType],
M.[From],
M.ArrivalDate as MessageArrivalDate,
M.ID as MessageID
FROM
@ListConversationType AS convType
INNER JOIN (
@ListMailboxId AS mailboxIds
INNER JOIN
[Mailbox] AS Mb ON (Mb.ID = mailboxIds.MailboxID)
INNER JOIN
[Conversation] as C
ON C.ID = Mb.ID
) ON convType.ID = C.[ConversationType]
INNER MERGE JOIN
[Customer] AS Cust ON (Cust.ID = C.CustomerID)
INNER MERGE JOIN
[ConversationHistory] AS CHis ON (CHis.ConversationID = C.ID)
INNER MERGE JOIN
[Message] AS M ON (M.ConversationID = C.ID)
WHERE
Mb.CompanyID = @CompanyID
AND ((CHis.CreatedOn > @fromDate
AND CHis.CreatedOn < @toDate
AND CHis.Activity = 1
AND CHis.TagData = '3')
OR (M.ArrivalDate > @fromDate
AND M.ArrivalDate < @toDate))
答案 1 :(得分:0)
50 DTU相当于1/2逻辑内核。 查看更多:Using the Azure SQL Database DTU Calculator
答案 2 :(得分:0)
本周我遇到了同样的问题,最终用户声称使用连接到Azure中托管的VM的应用程序速度很慢。另外,我拥有几乎相同的VM(4个CPU,14GB的RAM和S3,但具有100DTU)。
就我而言,我有很多索引,其中avg_fragmentation_in_percent
大于30,这导致执行存储过程时性能下降。
在SSMS中运行此命令,如果存在针对其运行存储过程的表的索引,那么您可以解决它:
SELECT dbschemas.[name] as 'Schema',
dbtables.[name] as 'Table',
dbindexes.[name] as 'Index',
indexstats.avg_fragmentation_in_percent,
indexstats.page_count
FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL, NULL, NULL) AS indexstats
INNER JOIN sys.tables dbtables on dbtables.[object_id] = indexstats.[object_id]
INNER JOIN sys.schemas dbschemas on dbtables.[schema_id] = dbschemas.[schema_id]
INNER JOIN sys.indexes AS dbindexes ON dbindexes.[object_id] = indexstats.[object_id]
WHERE indexstats.database_id = DB_ID()
AND indexstats.index_id = dbindexes.index_id
AND indexstats.avg_fragmentation_in_percent >30
--AND dbindexes.[name] like '%CLUSTER%'
ORDER BY indexstats.avg_fragmentation_in_percent DESC
更多信息here。
编辑:
还要检查统计信息的历史记录:
SELECT
sys.objects.name AS table_name,
sys.indexes.name as index_name,
sys.indexes.type_desc as index_type,
stats_date(sys.indexes.object_id,sys.indexes.index_id)
as last_update_stats_date,
DATEDIFF(d,stats_date(sys.indexes.object_id,sys.indexes.index_id),getdate())
as stats_age_in_days
FROM
sys.indexes
INNER JOIN sys.objects on sys.indexes.object_id=sys.objects.object_id
WHERE
sys.objects.type = 'U'
AND
sys.indexes.index_id > 0
--AND sys.indexes.name Like '%CLUSTER%'
ORDER BY
stats_age_in_days DESC;
GO