Azure SQL DB导致存储过程连接超时

时间:2018-07-06 05:53:20

标签: sql sql-server azure stored-procedures azure-sql-database

我们已经将数据库托管在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

这是执行计划:

Execution Plan

请提出您需要改进的建议?我们还需要升级我的价格等级吗?

理想情况下,对于14GB数据库,Azure定价层应该是什么?

3 个答案:

答案 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)。表变量没有统计信息,导致估计错误。
  • 确定第一个运算符,其中估计行与实际行之间的差异足够大。拆分查询。查询1: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)

  • 将OR谓词拆分为UNIONUNION 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