Come ottenere l'utilizzo della CPU dal database per un'istanza particolare?


15

Ho trovato le seguenti query per rilevare l'utilizzo della CPU da parte del database, ma mostrano risultati diversi:

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);

La query sopra dice che il problema riguarda uno dei miei database (quasi il 96%).

E la query seguente indica che il problema riguarda il database principale e quello di distribuzione (circa il 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

Ho verificato che sys.sysprocessessia stato depetrato. Questo significa che i risultati della seconda query sono sbagliati?

Risposte:


14

Mentre io, come @Thomas, sono completamente d'accordo con @Aaron nei commenti sulla domanda relativa alla preoccupazione che "l'utilizzo della CPU per database" sia accurato o utile, posso almeno rispondere alla domanda sul perché queste due query sono così diverso. E il motivo per cui sono diversi indicherà quale è più preciso, anche se quel livello di accuratezza più elevato è ancora relativo a quello che è specificamente inaccurato, quindi non è ancora veramente accurato ;-).

La prima query utilizza sys.dm_exec_query_stats per ottenere informazioni sulla CPU (ad es total_worker_time.). Se vai alla pagina collegata che è la documentazione MSDN per quel DMV vedrai una breve introduzione di 3 frasi e 2 di quelle frasi ci danno la maggior parte di ciò di cui abbiamo bisogno per capire il contesto di queste informazioni ("quanto è affidabile" e "come si confronta con sys.sysprocesses"). Queste due frasi sono:

Restituisce statistiche aggregate sulle prestazioni per i piani di query memorizzati nella cache in SQL Server. ... Quando un piano viene rimosso dalla cache, le righe corrispondenti vengono eliminate da questa vista

La prima frase, "Restituisce statistiche aggregate sulle prestazioni", ci dice che le informazioni in questo DMV (proprio come molti altri) sono cumulative e non specifiche solo per le query attualmente in esecuzione. Ciò è anche indicato da un campo in quel DMV che non fa parte della query nella Domanda execution_count, che mostra di nuovo che si tratta di dati cumulativi. Ed è abbastanza utile che questi dati siano cumulativi in ​​quanto è possibile ottenere medie, ecc. Dividendo alcune delle metriche per execution_count.

La seconda frase, "i piani che vengono rimossi dalla cache vengono rimossi anche da questo DMV", indicano che non è affatto un quadro completo, specialmente se il server ha già una cache piuttosto completa del piano ed è sotto carico e quindi sta scadendo i piani piuttosto frequentemente. Inoltre, la maggior parte dei DMV vengono reimpostati quando il server viene reimpostato, quindi non sono una cronologia vera anche se queste righe non sono state rimosse alla scadenza dei piani.

Ora confrontiamo quanto sopra con sys.sysprocesses. Questa vista di sistema mostra solo ciò che è attualmente in esecuzione, proprio come la combinazione di sys.dm_exec_connections , sys.dm_exec_sessions e sys.dm_exec_requests (che è indicato nella pagina collegata per sys.dm_exec_sessions). Questa è una visione completamente diversa del server rispetto al sys.dm_exec_query_statsDMV che contiene i dati anche dopo il completamento del processo. Significato, in relazione a "i risultati della seconda query sono errati?" domanda, non si sbagliano, riguardano solo un aspetto diverso (ad es. il lasso di tempo) delle statistiche delle prestazioni.

Quindi, la query che utilizza sys.sysprocessessta solo guardando "adesso". E la query che utilizza sys.dm_exec_query_statsguarda principalmente (forse) cosa è successo dall'ultimo riavvio del servizio SQL Server (o ovviamente riavvio del sistema). Per l'analisi generale delle prestazioni sembra che sys.dm_exec_query_statssia molto meglio, ma di nuovo, rilascia continuamente informazioni utili. E, in entrambi i casi, è necessario considerare anche i punti sollevati da @Aaron nei commenti alle domande (poiché rimossi) per quanto riguarda l'accuratezza del valore "database_id" in primo luogo (ovvero riflette solo il DB attivo che ha avviato il codice , non necessariamente dove si verifica il "problema").

Ma, se avete solo bisogno / voglia di ottenere un senso di ciò che sta accadendo in questo momento in tutti i database, forse perché le cose stanno rallentando in questo momento, si sta meglio utilizzando la combinazione di sys.dm_exec_connections, sys.dm_exec_sessionse sys.dm_exec_requests(e non il deprecato sys.sysprocesses). Tieni presente che stai cercando / per query , non per database , perché le query possono unirsi su più database, includere UDF da uno o più database, ecc.


EDIT:
se la preoccupazione generale è ridurre i consumatori di CPU elevati, cerca le query che occupano la maggior parte della CPU, perché i database non occupano effettivamente la CPU (la ricerca per database potrebbe funzionare in una società di hosting in cui ogni database è isolato e di proprietà di un altro cliente).

La query seguente aiuterà a identificare le query con un utilizzo medio elevato della CPU. Condensa i dati nel DMV query_stats poiché tali record possono mostrare più volte la stessa query (sì, lo stesso sottoinsieme del batch di query), ciascuno con un piano di esecuzione diverso.

;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;

è AvgCPUin millisecondi?
Kolob Canyon,

Ciao @KolobCanyon. Secondo la documentazione di sys.dm_exec_query_stats , total_worker_timeè " La quantità totale di tempo della CPU, riportata in microsecondi (ma accurata solo in millisecondi), che è stata consumata dalle esecuzioni di questo piano da quando è stata compilata. ". Questo aiuta? Potrebbe essere facilmente convertito in millisecondi se è quello che vuoi vedere.
Solomon Rutzky,

1

Ho modificato la query per la divisione di 0 errori e ottimizzato i nomi delle colonne per copiare / incollare in Excel.

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
GO
WITH DB_CPU_Stats
AS
(
    SELECT DatabaseID, isnull(DB_Name(DatabaseID),case DatabaseID when 32767 then 'Internal ResourceDB' else CONVERT(varchar(255),DatabaseID)end) AS [DatabaseName], 
      SUM(total_worker_time) AS [CPU_Time_Ms],
      SUM(total_logical_reads)  AS [Logical_Reads],
      SUM(total_logical_writes)  AS [Logical_Writes],
      SUM(total_logical_reads+total_logical_writes)  AS [Logical_IO],
      SUM(total_physical_reads)  AS [Physical_Reads],
      SUM(total_elapsed_time)  AS [Duration_MicroSec],
      SUM(total_clr_time)  AS [CLR_Time_MicroSec],
      SUM(total_rows)  AS [Rows_Returned],
      SUM(execution_count)  AS [Execution_Count],
      count(*) 'Plan_Count'
    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 [Rank_CPU],
       DatabaseName,
       [CPU_Time_Hr] = convert(decimal(15,2),([CPU_Time_Ms]/1000.0)/3600) ,
        CAST([CPU_Time_Ms] * 1.0 / SUM(case [CPU_Time_Ms] when 0 then 1 else [CPU_Time_Ms] end) OVER() * 100.0 AS DECIMAL(5, 2)) AS [CPU_Percent],
       [Duration_Hr] = convert(decimal(15,2),([Duration_MicroSec]/1000000.0)/3600) , 
       CAST([Duration_MicroSec] * 1.0 / SUM(case [Duration_MicroSec] when 0 then 1 else [Duration_MicroSec] end) OVER() * 100.0 AS DECIMAL(5, 2)) AS [Duration_Percent],    
       [Logical_Reads],
        CAST([Logical_Reads] * 1.0 / SUM(case [Logical_Reads] when 0 then 1 else [Logical_Reads] end) OVER() * 100.0 AS DECIMAL(5, 2)) AS [Logical_Reads_Percent],      
       [Rows_Returned],
        CAST([Rows_Returned] * 1.0 / SUM(case [Rows_Returned] when 0 then 1 else [Rows_Returned] end) OVER() * 100.0 AS DECIMAL(5, 2)) AS [Rows_Returned_Percent],
       [Reads_Per_Row_Returned] = [Logical_Reads]/(case [Rows_Returned] when 0 then 1 else [Rows_Returned] end),
       [Execution_Count],
        CAST([Execution_Count] * 1.0 / SUM(case [Execution_Count]  when 0 then 1 else [Execution_Count] end) OVER() * 100.0 AS DECIMAL(5, 2)) AS [Execution_Count_Percent],
       [Physical_Reads],
       CAST([Physical_Reads] * 1.0 / SUM(case [Physical_Reads] when 0 then 1 else [Physical_Reads] end ) OVER() * 100.0 AS DECIMAL(5, 2)) AS [Physcal_Reads_Percent], 
       [Logical_Writes],
        CAST([Logical_Writes] * 1.0 / SUM(case [Logical_Writes] when 0 then 1 else [Logical_Writes] end) OVER() * 100.0 AS DECIMAL(5, 2)) AS [Logical_Writes_Percent],
       [Logical_IO],
        CAST([Logical_IO] * 1.0 / SUM(case [Logical_IO] when 0 then 1 else [Logical_IO] end) OVER() * 100.0 AS DECIMAL(5, 2)) AS [Logical_IO_Percent],
       [CLR_Time_MicroSec],
       CAST([CLR_Time_MicroSec] * 1.0 / SUM(case [CLR_Time_MicroSec] when 0 then 1 else [CLR_Time_MicroSec] end ) OVER() * 100.0 AS DECIMAL(5, 2)) AS [CLR_Time_Percent],
       [CPU_Time_Ms],[CPU_Time_Ms]/1000 [CPU_Time_Sec],
       [Duration_MicroSec],[Duration_MicroSec]/1000000 [Duration_Sec]
FROM DB_CPU_Stats
WHERE DatabaseID > 4 -- system databases
AND DatabaseID <> 32767 -- ResourceDB
ORDER BY [Rank_CPU] OPTION (RECOMPILE);

0

La query CPU mi è piaciuta sys.dm_exec_query_statscosì tanto che l'ho estesa. È ancora classificato in base alla CPU ma ho aggiunto altri totali e percentuali per ottenere un profilo server migliore. Questo si copia bene in Excel e con la formattazione del colore condizionale nelle colonne Percentuale, i numeri peggiori si distinguono bene. Ho usato la "Scala colori graduata" con 3 colori; un colore rosa per valori alti, giallo per medio, verde per basso.

Ho aggiunto un'etichetta per database id 32676cui è il database di risorse SQL interno. Converto CPU e durata in ore per ottenere un migliore senso dell'utilizzo del tempo.

WITH DB_CPU_Stats
AS
(
    SELECT DatabaseID, isnull(DB_Name(DatabaseID),case DatabaseID when 32767 then 'Internal ResourceDB' else CONVERT(varchar(255),DatabaseID)end) AS [DatabaseName], 
      SUM(total_worker_time) AS [CPU Time Ms],
      SUM(total_logical_reads)  AS [Logical Reads],
      SUM(total_logical_writes)  AS [Logical Writes],
      SUM(total_logical_reads+total_logical_writes)  AS [Logical IO],
      SUM(total_physical_reads)  AS [Physical Reads],
      SUM(total_elapsed_time)  AS [Duration MicroSec],
      SUM(total_clr_time)  AS [CLR Time MicroSec],
      SUM(total_rows)  AS [Rows Returned],
      SUM(execution_count)  AS [Execution Count],
      count(*) 'Plan Count'

    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 [Rank CPU],
       DatabaseName,
       [CPU Time Hr] = convert(decimal(15,2),([CPU Time Ms]/1000.0)/3600) ,
        CAST([CPU Time Ms] * 1.0 / SUM([CPU Time Ms]) OVER() * 100.0 AS DECIMAL(5, 2)) AS [CPU Percent],
       [Duration Hr] = convert(decimal(15,2),([Duration MicroSec]/1000000.0)/3600) , 
       CAST([Duration MicroSec] * 1.0 / SUM([Duration MicroSec]) OVER() * 100.0 AS DECIMAL(5, 2)) AS [Duration Percent],    
       [Logical Reads],
        CAST([Logical Reads] * 1.0 / SUM([Logical Reads]) OVER() * 100.0 AS DECIMAL(5, 2)) AS [Logical Reads Percent],      
       [Rows Returned],
        CAST([Rows Returned] * 1.0 / SUM([Rows Returned]) OVER() * 100.0 AS DECIMAL(5, 2)) AS [Rows Returned Percent],
       [Reads Per Row Returned] = [Logical Reads]/[Rows Returned],
       [Execution Count],
        CAST([Execution Count] * 1.0 / SUM([Execution Count]) OVER() * 100.0 AS DECIMAL(5, 2)) AS [Execution Count Percent],
       [Physical Reads],
       CAST([Physical Reads] * 1.0 / SUM([Physical Reads]) OVER() * 100.0 AS DECIMAL(5, 2)) AS [Physcal Reads Percent], 
       [Logical Writes],
        CAST([Logical Writes] * 1.0 / SUM([Logical Writes]) OVER() * 100.0 AS DECIMAL(5, 2)) AS [Logical Writes Percent],
       [Logical IO],
        CAST([Logical IO] * 1.0 / SUM([Logical IO]) OVER() * 100.0 AS DECIMAL(5, 2)) AS [Logical IO Percent],
       [CLR Time MicroSec],
       CAST([CLR Time MicroSec] * 1.0 / SUM(case [CLR Time MicroSec] when 0 then 1 else [CLR Time MicroSec] end ) OVER() * 100.0 AS DECIMAL(5, 2)) AS [CLR Time Percent],
       [CPU Time Ms],[CPU Time Ms]/1000 [CPU Time Sec],
       [Duration MicroSec],[Duration MicroSec]/1000000 [Duration Sec]
FROM DB_CPU_Stats
--WHERE DatabaseID > 4 -- system databases
--AND DatabaseID <> 32767 -- ResourceDB
ORDER BY [Rank CPU] OPTION (RECOMPILE);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.