Wie erhalte ich die CPU-Auslastung nach Datenbank für eine bestimmte Instanz?

15

Ich habe die folgenden Abfragen gefunden, um die CPU-Auslastung nach Datenbank zu ermitteln, aber sie zeigen unterschiedliche Ergebnisse:

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

Aus der obigen Abfrage geht hervor, dass das Problem bei einer meiner Datenbanken liegt (fast 96%).

Aus der folgenden Abfrage geht hervor, dass das Problem beim Master und den Distributionsdatenbanken liegt (ca. 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

Ich habe nachgesehen, ob das sys.sysprocessesdekrementiert ist. Bedeutet dies, dass die Ergebnisse der zweiten Abfrage falsch sind?

gotqn
quelle

Antworten:

14

Ich stimme @Aaron wie @Thomas in den Kommentaren zu der Frage zu, ob die Bedenken hinsichtlich der "CPU-Auslastung pro Datenbank" zutreffend oder nützlich sind, kann aber zumindest die Frage beantworten, warum diese beiden Fragen so sind anders. Und der Grund, warum sie unterschiedlich sind, zeigt an, welches genauer ist, obwohl dieses höhere Maß an Genauigkeit immer noch relativ zu dem ist, das spezifisch ungenau ist und daher immer noch nicht wirklich genau ist ;-).

Die erste Abfrage verwendet sys.dm_exec_query_stats , um CPU-Informationen (dh total_worker_time) abzurufen . Wenn Sie zu der verlinkten Seite gehen, die die MSDN-Dokumentation für dieses DMV ist, sehen Sie ein kurzes 3-Satz-Intro und 2 dieser Sätze geben uns das Meiste, was wir zum Verstehen des Kontexts dieser Informationen benötigen ("wie zuverlässig ist es"). und "wie vergleicht es sich mit sys.sysprocesses"). Diese beiden Sätze sind:

Gibt zusammengefasste Leistungsstatistiken für zwischengespeicherte Abfragepläne in SQL Server zurück. ... Wenn ein Plan aus dem Cache entfernt wird, werden die entsprechenden Zeilen aus dieser Ansicht entfernt

Der erste Satz "Gibt aggregierte Leistungsstatistiken zurück" besagt, dass die Informationen in dieser DMV (genau wie in mehreren anderen) kumulativ sind und sich nicht nur auf aktuell ausgeführte Abfragen beziehen. Dies wird auch durch ein Feld in der DMV angezeigt, das nicht Teil der Abfrage in der Frage execution_countist. Dies zeigt wiederum, dass es sich um kumulative Daten handelt. Und es ist sehr praktisch, wenn diese Daten kumulativ sind, da Sie Durchschnittswerte usw. erhalten können, indem Sie einige der Metriken durch dividieren execution_count.

Der zweite Satz, "Pläne, die aus dem Cache entfernt werden, werden auch aus dieser DMV entfernt", gibt an, dass es sich überhaupt nicht um ein vollständiges Bild handelt, insbesondere wenn der Server bereits über einen ziemlich vollen Plan-Cache verfügt und ausgelastet ist und daher Pläne ablaufen etwas häufig. Außerdem werden die meisten DMVs zurückgesetzt, wenn der Server zurückgesetzt wird, sodass sie keine echte Historie darstellen, selbst wenn diese Zeilen bei Ablauf der Pläne nicht entfernt wurden.

Vergleichen wir nun das Obige mit sys.sysprocesses. Diese Systemansicht zeigt nur, was gerade ausgeführt wird, genau wie die Kombination aus sys.dm_exec_connections , sys.dm_exec_sessions und sys.dm_exec_requests (die auf der verknüpften Seite für angegeben ist sys.dm_exec_sessions). Dies ist eine völlig andere Ansicht des Servers als der sys.dm_exec_query_statsDMV, der die Daten auch nach Abschluss des Vorgangs enthält. Das heißt, in Bezug auf "sind die Ergebnisse der zweiten Abfrage falsch?" Frage, sie sind nicht falsch, sie beziehen sich nur auf einen anderen Aspekt (dh Zeitrahmen) der Leistungsstatistik.

Bei der Abfrage mit sys.sysprocesseswird also nur "im Moment" gesucht. Und die Abfrage, die verwendet sys.dm_exec_query_statswird, untersucht hauptsächlich (möglicherweise), was seit dem letzten Neustart des SQL Server-Dienstes (oder offensichtlich seit dem Neustart des Systems) geschehen ist. Für die allgemeine Leistungsanalyse scheint dies sys.dm_exec_query_statsweitaus besser zu sein, aber es werden immer wieder nützliche Informationen gelöscht. In beiden Fällen müssen Sie auch die Punkte berücksichtigen, die @Aaron in den (seitdem entfernten) Fragenkommentaren bezüglich der Genauigkeit des Werts "database_id" gemacht hat (dh, es gibt nur die aktive Datenbank wieder, die den Code initiiert hat , nicht unbedingt dort, wo das "Problem" auftritt).

Aber, wenn Sie nur brauchen / wollen ein Gefühl dafür zu bekommen , was in allen Datenbanken im Augenblick geschieht, möglicherweise , weil die Dinge verlangsamen jetzt nach unten, sind Sie besser dran , die Kombination der Verwendung sys.dm_exec_connections, sys.dm_exec_sessionsund sys.dm_exec_requests(und nicht die veralteten sys.sysprocesses). Denken Sie nur daran, dass Sie nach Abfragen suchen , nicht nach Datenbanken , da Abfragen über mehrere Datenbanken hinweg verknüpft werden können, UDFs aus einer oder mehreren Datenbanken enthalten usw.


BEARBEITEN:
Wenn das Hauptanliegen darin besteht, die Anzahl der CPU-Verbraucher zu verringern, suchen Sie nach den Abfragen, die die meiste CPU beanspruchen, da Datenbanken tatsächlich keine CPU beanspruchen im Besitz eines anderen Kunden).

Mit der folgenden Abfrage können Sie Abfragen mit einer hohen durchschnittlichen CPU-Auslastung identifizieren. Die Daten in der DMV "query_stats" werden komprimiert, da diese Datensätze dieselbe Abfrage (ja, dieselbe Teilmenge des Abfragestapels) mehrmals mit jeweils einem anderen Ausführungsplan anzeigen können.

;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;
Solomon Rutzky
quelle
ist AvgCPUin Millisekunden?
Kolob Canyon
Hi @KolobCanyon. In der Dokumentation für sys.dm_exec_query_stats heißt total_worker_timees: " Die Gesamtmenge der CPU-Zeit, angegeben in Mikrosekunden (aber nur millisekundengenau), die von den Ausführungen dieses Plans seit seiner Kompilierung verbraucht wurde. " Hilft das? Das kann leicht in Millisekunden umgewandelt werden, wenn Sie das so sehen möchten.
Solomon Rutzky
1

Ich habe die Abfrage für die Division durch 0 Fehler und optimierte Spaltennamen für das Kopieren / Einfügen in Excel angepasst.

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);
HakanM
quelle
0

Die CPU-Abfrage hat sys.dm_exec_query_statsmir so gut gefallen, dass ich sie erweitert habe. Es ist immer noch nach CPU geordnet, aber ich habe andere Summen und Prozente hinzugefügt, um ein besseres Serverprofil zu erhalten. Dies kopiert sich gut in Excel und mit der bedingten Farbformatierung in den Prozent-Spalten fallen die schlechtesten Zahlen gut auf. Ich habe die abgestufte Farbskala mit drei Farben verwendet. eine rosafarbene Farbe für hohe Werte, gelb für mittlere, grün für niedrige.

Ich habe eine Bezeichnung hinzugefügt, für database id 32676die die interne SQL-Ressourcendatenbank angegeben ist. Ich konvertiere die CPU- und die Dauer-Zeit in Stunden, um ein besseres Gefühl für die Zeitnutzung zu erhalten.

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);
Drew Neff
quelle