共用方式為


針對似乎從未在 SQL Server 中結束的查詢進行疑難解答

本文說明您有查詢似乎永遠不會完成的問題疑難解答步驟,或讓查詢完成可能需要數小時或數天的時間。

什麼是永不結束的查詢?

本檔著重於繼續執行或編譯的查詢,也就是其 CPU 繼續增加。 它不適用於封鎖或等候某些從未釋放的資源的查詢(CPU 會維持不變或變更很少)。

重要

如果查詢保留以完成其執行,它最終就會完成。 可能需要幾秒鐘的時間,或者可能需要幾天的時間。

「永不結束」一詞是用來描述當事實上查詢最終完成時未完成查詢的感知。

識別永不結束的查詢

若要識別查詢是否持續執行或停滯在瓶頸上,請遵循下列步驟:

  1. 執行下列查詢:

    DECLARE @cntr int = 0
    
    WHILE (@cntr < 3)
    BEGIN
        SELECT TOP 10 s.session_id,
                        r.status,
                        r.wait_time,
                        r.wait_type,
                        r.wait_resource,
                        r.cpu_time,
                        r.logical_reads,
                        r.reads,
                        r.writes,
                        r.total_elapsed_time / (1000 * 60) 'Elaps M',
                        SUBSTRING(st.TEXT, (r.statement_start_offset / 2) + 1,
                        ((CASE r.statement_end_offset
                            WHEN -1 THEN DATALENGTH(st.TEXT)
                            ELSE r.statement_end_offset
                        END - r.statement_start_offset) / 2) + 1) AS statement_text,
                        COALESCE(QUOTENAME(DB_NAME(st.dbid)) + N'.' + QUOTENAME(OBJECT_SCHEMA_NAME(st.objectid, st.dbid)) 
                        + N'.' + QUOTENAME(OBJECT_NAME(st.objectid, st.dbid)), '') AS command_text,
                        r.command,
                        s.login_name,
                        s.host_name,
                        s.program_name,
                        s.last_request_end_time,
                        s.login_time,
                        r.open_transaction_count,
                        atrn.name as transaction_name,
                        atrn.transaction_id,
                        atrn.transaction_state
            FROM sys.dm_exec_sessions AS s
            JOIN sys.dm_exec_requests AS r ON r.session_id = s.session_id 
                    CROSS APPLY sys.Dm_exec_sql_text(r.sql_handle) AS st
            LEFT JOIN (sys.dm_tran_session_transactions AS stran 
                 JOIN sys.dm_tran_active_transactions AS atrn
                    ON stran.transaction_id = atrn.transaction_id)
            ON stran.session_id =s.session_id
            WHERE r.session_id != @@SPID
            ORDER BY r.cpu_time DESC
    
        SET @cntr = @cntr + 1
    WAITFOR DELAY '00:00:05'
    END
    
  2. 檢查範例輸出。

    • 當您注意到類似下列輸出的輸出時,本文中的疑難解答步驟特別適用,其中 CPU 會隨著經過的時間成比例增加,而不會有顯著的等候時間。 請務必注意,在此案例中 logical_reads 所做的變更並不相關,因為某些 CPU 系結 T-SQL 要求可能完全不會執行任何邏輯讀取(例如執行計算或 WHILE 迴圈)。

      session_id status cpu_time logical_reads wait_time wait_type
      56 執行中 7038 101000 0 NULL
      56 可執行的 12040 301000 0 NULL
      56 執行中 17020 523000 0 NULL
    • 如果您觀察到類似下列等候案例,且 CPU 不會稍微變更或變更,且會話正在等候資源,則本文不適用。

      session_id status cpu_time logical_reads wait_time wait_type
      56 暫止 0 3 8312 LCK_M_U
      56 暫止 0 3 13318 LCK_M_U
      56 暫止 0 5 18331 LCK_M_U

    如需詳細資訊,請參閱 診斷等候或瓶頸

編譯時間長

在極少數情況下,您可能會發現 CPU 會隨著時間持續增加,但不是由查詢執行所驅動。 相反地,它可能是由過多的編譯所驅動(查詢的剖析和編譯)。 在這些情況下,請檢查 transaction_name 輸出數據行,並尋找的值 sqlsource_transform。 此交易名稱表示編譯。

收集診斷資料

若要使用 SQL Server Management Studio (SSMS) 收集診斷數據,請遵循下列步驟:

  1. 擷取預估的查詢執行計劃 XML。

  2. 檢閱查詢計劃,以查看是否有任何明顯的指示,指出速度緩慢是否來自何處。 一般範例包括:

    • 數據表或索引掃描(查看估計的數據列)。
    • 由大型外部數據表數據集驅動的巢狀迴圈。
    • 巢狀迴圈與循環內部有大型分支的巢狀迴圈。
    • 數據表多任務緩衝處理。
    • 清單中需要很長的時間來處理每個數據列的 SELECT 函式。
  3. 如果查詢隨時快速執行,您可以擷取要比較的實際 XML 執行計劃「快速」執行

檢閱所收集計劃的方法

本節將說明如何檢閱收集的數據。 它會使用 SQL Server 2016 SP1 和更新版本的多個 XML 查詢計劃(使用擴充功能 *.sqlplan)。

請遵循下列步驟來 比較執行計劃

  1. 開啟先前儲存的查詢執行計劃檔案 (.sqlplan)。

  2. 以滑鼠右鍵按兩下執行計畫的空白區域,然後選取 [ 比較執行程式表]。

  3. 選擇您想要比較的第二個查詢計劃檔案。

  4. 尋找粗箭號,指出在運算符之間流動大量的數據列。 然後選取箭號前後的運算符,並比較兩個計劃的實際數據列數目

  5. 比較第二個和第三個計劃,以查看數據列的最大流程是否發生在相同的運算符中。

    以下是範例:

    比較 SSMS 中的查詢計劃。

解決方法

  1. 請確定已更新查詢中使用的數據表統計數據。

  2. 在查詢計劃中尋找遺漏的索引建議,並套用任何索引。

  3. 以簡化查詢的目標重寫查詢:

    • 使用更選擇性 WHERE 的述詞來減少預先處理的數據。
    • 將其分開。
    • 在臨時表中選取一些元件,稍後再聯結這些元件。
    • 由於優化器數據列目標,在TOP長時間執行的查詢中移除 、 EXISTSFAST (T-SQL)。 或者,您可以使用DISABLE_OPTIMIZER_ROWGOAL提示。 如需詳細資訊,請參閱 數據列目標消失的流氓
    • 避免在將語句結合成單一大型查詢時,在這類情況下使用通用數據表運算式(CTE)。
  4. 請嘗試使用 查詢提示 來產生更好的計劃:

    • HASH JOINMERGE JOIN 提示
    • FORCE ORDER 提示
    • FORCESEEK 提示
    • RECOMPILE
    • 如果您有可以強制的快速查詢計劃,請使用PLAN N'<xml_plan>'
  5. 如果這類計劃存在,且您的 SQL Server 版本支援 查詢存放區,請使用 查詢存放區 (QDS) 強制良好的已知計劃。

診斷等候或瓶頸

如果問題不是長時間執行的CPU驅動查詢,本節會在此包含為參考。 您可以使用它來針對因等候而長時間的查詢進行疑難解答。

若要優化等候瓶頸的查詢,請找出等候的時間長度,以及瓶頸所在的位置(等候類型)。 確認等候類型之後,請減少等候時間,或完全排除等候。

若要計算大約等候時間,請從查詢經過的時間減去 CPU 時間(背景工作時間)。 一般而言,CPU 時間是實際運行時間,而查詢存留期的剩餘部分正在等候。

如何計算近似等候持續時間的範例:

經過的時間 (毫秒) CPU 時間 (毫秒) 等候時間 (毫秒)
3200 3000 200
7080 1000 6080

識別瓶頸或等候

  • 若要識別歷程記錄長時間等候的查詢(例如, >20% 的整體經過時間是等候時間),請執行下列查詢。 此查詢會針對 SQL Server 開頭之後的快取查詢計劃使用效能統計數據。

    SELECT t.text,
             qs.total_elapsed_time / qs.execution_count
             AS avg_elapsed_time,
             qs.total_worker_time / qs.execution_count
             AS avg_cpu_time,
             (qs.total_elapsed_time - qs.total_worker_time) / qs.execution_count
             AS avg_wait_time,
             qs.total_logical_reads / qs.execution_count
             AS avg_logical_reads,
             qs.total_logical_writes / qs.execution_count
             AS avg_writes,
             qs.total_elapsed_time
             AS cumulative_elapsed_time
    FROM sys.dm_exec_query_stats qs
             CROSS apply sys.Dm_exec_sql_text (sql_handle) t
    WHERE (qs.total_elapsed_time - qs.total_worker_time) / qs.total_elapsed_time
             > 0.2
    ORDER BY qs.total_elapsed_time / qs.execution_count DESC
    
  • 若要識別目前執行超過 500 毫秒的查詢,請執行下列查詢:

    SELECT r.session_id, r.wait_type, r.wait_time AS wait_time_ms
    FROM sys.dm_exec_requests r 
       JOIN sys.dm_exec_sessions s ON r.session_id = s.session_id 
    WHERE wait_time > 500
    AND is_user_process = 1
    
  • 如果可以收集查詢計劃,請檢查 SSMS 中執行計劃屬性中的 WaitStats

    1. 使用 [包含實際執行計劃] 執行查詢
    2. 在 [執行計劃] 索引標籤中,以滑鼠右鍵按兩下最左邊的運算子
    3. 選取 [屬性],然後選取 [WaitStats] 屬性。
    4. 檢查 WaitTimeMsWaitType
  • 如果您熟悉 PSSDiag/SQLdiagSQL LogScout LightPerf/GeneralPerf 案例,請考慮使用其中一個來收集效能統計數據,並識別 SQL Server 實例上的等候查詢。 您可以匯入收集的數據檔,並使用 SQL Nexus 分析效能數據

協助消除或減少等候的參考

每個等候類型的原因和解決方式會有所不同。 沒有一個一般方法可以解析所有等候類型。 以下是疑難解答和解決常見等候類型問題的文章:

如需許多 Wait 類型及其指示的描述,請參閱 Waits 類型中的表格。