Sql-Server

如何獲取特定實例的數據庫 CPU 使用率?

  • February 19, 2021

我發現以下查詢來檢測數據庫的 CPU 使用情況,但它們顯示不同的結果:

WITH DB_CPU_Stats
AS
(
   SELECT DatabaseID, DB_Name(DatabaseID) AS [DatabaseName], 
     SUM(total_worker_time) AS [CPU_Time_Ms]
   FROM sys.dm_exec_query_stats AS qs
   CROSS APPLY (
                   SELECT CONVERT(int, value) AS [DatabaseID] 
                 FROM sys.dm_exec_plan_attributes(qs.plan_handle)
                 WHERE attribute = N'dbid') AS F_DB
   GROUP BY DatabaseID
)
SELECT ROW_NUMBER() OVER(ORDER BY [CPU_Time_Ms] DESC) AS [row_num],
      DatabaseName,
       [CPU_Time_Ms], 
      CAST([CPU_Time_Ms] * 1.0 / SUM([CPU_Time_Ms]) OVER() * 100.0 AS DECIMAL(5, 2)) AS [CPUPercent]
FROM DB_CPU_Stats
--WHERE DatabaseID > 4 -- system databases
--AND DatabaseID <> 32767 -- ResourceDB
ORDER BY row_num OPTION (RECOMPILE);

上面的查詢表明問題出在我的一個數據庫中(幾乎 96%)。

下面的查詢表明問題出在主數據庫和分發數據庫上(大約 90%):

DECLARE @total INT
SELECT @total=sum(cpu) FROM sys.sysprocesses sp (NOLOCK)
   join sys.sysdatabases sb (NOLOCK) ON sp.dbid = sb.dbid

SELECT sb.name 'database', @total 'system cpu', SUM(cpu) 'database cpu', CONVERT(DECIMAL(4,1), CONVERT(DECIMAL(17,2),SUM(cpu)) / CONVERT(DECIMAL(17,2),@total)*100) '%'
FROM sys.sysprocesses sp (NOLOCK)
JOIN sys.sysdatabases sb (NOLOCK) ON sp.dbid = sb.dbid
--WHERE sp.status = 'runnable'
GROUP BY sb.name
ORDER BY CONVERT(DECIMAL(4,1), CONVERT(DECIMAL(17,2),SUM(cpu)) / CONVERT(DECIMAL(17,2),@total)*100) desc

我已經檢查過sys.sysprocesses它已被棄用。這是否意味著第二個查詢的結果是錯誤的?

雖然我和@Thomas 一樣,完全同意@Aaron 關於“每個數據庫的CPU 使用率”是否準確或有用的問題的評論,但我至少可以回答為什麼這兩個查詢如此的問題不同的。它們不同的原因將表明哪一個更準確,儘管更高的準確度仍然相對於特別不准確的那個,因此仍然不是真正準確的;-)。

第一個查詢使用sys.dm_exec_query_stats來獲取 CPU 資訊(即total_worker_time)。如果您轉到該 DMV 的 MSDN 文件的連結頁面,您將看到一個簡短的 3 個句子介紹,其中 2 個句子為我們提供了理解此資訊上下文所需的大部分內容(“它有多可靠”和“與它相比如何sys.sysprocesses”)。這兩句話是:

返回 SQL Server 中記憶體查詢計劃的聚合性能統計資訊。…當從記憶體中刪除計劃時,相應的行會從此視圖中刪除

第一句“返回聚合性能統計資訊”告訴我們,這個 DMV 中的資訊(就像其他幾個一樣)是累積的,而不是特定於目前正在執行的查詢。這也由該 DMV 中的一個欄位表示,該欄位不是問題中查詢的一部分execution_count,這再次表明這是累積數據。將這些數據累積起來非常方便,因為您可以通過將一些指標除以execution_count.

第二句,“從記憶體中刪除的計劃也從這個 DMV 中刪除”,表明它根本不是一個完整的圖片,特別是如果伺服器已經有一個非常完整的計劃記憶體並且正在載入並且因此正在到期的計劃有點頻繁。此外,大多數 DMV 在伺服器重置時會被重置,因此即使這些行在計劃到期時沒有被刪除,它們也不是真實的歷史記錄。

現在讓我們將上面的內容與sys.sysprocesses. 此系統視圖僅顯示目前正在執行的內容,就像sys.dm_exec_connectionssys.dm_exec_sessionssys.dm_exec_requests的組合(在 的連結頁面上說明sys.dm_exec_sessions)。與 DMV相比,這是一個完全不同的伺服器視圖,sys.dm_exec_query_statsDMV 甚至在過程完成後也保存數據。意思是,關於“第二個查詢的結果是否錯誤?” 問題,它們沒有錯,它們只是與性能統計數據的不同方面(即時間框架)有關。

因此,使用的查詢sys.sysprocesses僅查看“現在”。並且使用的查詢主要sys.dm_exec_query_stats(可能)查看自上次重新啟動 SQL Server 服務(或顯然是系統重新啟動)以來發生的情況。對於一般性能分析,它似乎要好得多,但同樣,它總是會失去有用的資訊。而且,在這兩種情況下,您還需要首先考慮@Aaron 在問題評論(因為已刪除)中關於“database_id”值的準確性提出的觀點(即它只反映啟動程式碼的活動數據庫,不一定是“問題”發生的地方)。sys.dm_exec_query_stats

但是,如果您只是需要/想要了解所有數據庫中正在發生的事情,可能是因為現在事情正在放緩,那麼您最好使用sys.dm_exec_connectionssys.dm_exec_sessionssys.dm_exec_requests(而不是已棄用的sys.sysprocesses)的組合。請記住,您正在查看/查詢查詢,而不是數據庫,因為查詢可以跨多個數據庫連接,包括來自一個或多個數據庫的 UDF 等。


編輯:

如果總體關注的是減少高 CPU 消耗,那麼尋找佔用最多 CPU 的查詢,因為數據庫實際上並不佔用 CPU(查看每個數據庫可能在每個數據庫被隔離的託管公司工作,並且由不同的客戶擁有)。

以下查詢將有助於辨識平均 CPU 使用率較高的查詢。它壓縮了 query_stats DMV 中的數據,因為這些記錄可以多次顯示相同的查詢(是的,查詢批處理的相同子集),每次都有不同的執行計劃。

;WITH cte AS
(
 SELECT stat.[sql_handle],
        stat.statement_start_offset,
        stat.statement_end_offset,
        COUNT(*) AS [NumExecutionPlans],
        SUM(stat.execution_count) AS [TotalExecutions],
        ((SUM(stat.total_logical_reads) * 1.0) / SUM(stat.execution_count)) AS [AvgLogicalReads],
        ((SUM(stat.total_worker_time) * 1.0) / SUM(stat.execution_count)) AS [AvgCPU]
 FROM sys.dm_exec_query_stats stat
 GROUP BY stat.[sql_handle], stat.statement_start_offset, stat.statement_end_offset
)
SELECT CONVERT(DECIMAL(15, 5), cte.AvgCPU) AS [AvgCPU],
      CONVERT(DECIMAL(15, 5), cte.AvgLogicalReads) AS [AvgLogicalReads],
      cte.NumExecutionPlans,
      cte.TotalExecutions,
      DB_NAME(txt.[dbid]) AS [DatabaseName],
      OBJECT_NAME(txt.objectid, txt.[dbid]) AS [ObjectName],
      SUBSTRING(txt.[text], (cte.statement_start_offset / 2) + 1,
      (
        (CASE cte.statement_end_offset 
          WHEN -1 THEN DATALENGTH(txt.[text])
          ELSE cte.statement_end_offset
         END - cte.statement_start_offset) / 2
        ) + 1
      )
FROM cte
CROSS APPLY sys.dm_exec_sql_text(cte.[sql_handle]) txt
ORDER BY cte.AvgCPU DESC;

引用自:https://dba.stackexchange.com/questions/83058