Comment obtenir l'utilisation du processeur par la base de données pour une instance particulière?

15

J'ai trouvé les requêtes suivantes pour détecter l'utilisation du processeur par la base de données, mais elles affichent des résultats différents:

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 requête ci-dessus indique que le problème concerne l'une de mes bases de données (près de 96%).

Et la requête ci-dessous indique que le problème concerne le maître et les bases de données de distribution (environ 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

J'ai vérifié que le sys.sysprocessesdisque est décréé. Est-ce à dire que les résultats de la deuxième requête sont erronés?

gotqn
la source

Réponses:

14

Bien que, comme @Thomas, je sois entièrement d'accord avec @Aaron dans les commentaires sur la question concernant les "utilisations du processeur par base de données" qui sont soit précises soit utiles, je peux au moins répondre à la question de savoir pourquoi ces deux requêtes sont si différent. Et la raison pour laquelle ils sont différents indiquera lequel est le plus précis, bien que ce niveau de précision plus élevé soit toujours relatif à celui qui est spécifiquement inexact, donc toujours pas vraiment précis ;-).

La première requête utilise sys.dm_exec_query_stats pour obtenir les informations CPU (ie total_worker_time). Si vous allez sur la page liée qui est la documentation MSDN pour ce DMV, vous verrez une courte introduction de 3 phrases et 2 de ces phrases nous donnent la plupart de ce dont nous avons besoin pour comprendre le contexte de cette information ("quelle est la fiabilité" et "comment se compare-t-il àsys.sysprocesses "). Ces deux phrases sont:

Renvoie des statistiques de performances agrégées pour les plans de requête mis en cache dans SQL Server. ... Lorsqu'un plan est supprimé du cache, les lignes correspondantes sont supprimées de cette vue

La première phrase, "Renvoie des statistiques de performances agrégées ", nous indique que les informations contenues dans ce DMV (comme plusieurs autres) sont cumulatives et ne sont pas spécifiques aux seules requêtes en cours d'exécution. Cela est également indiqué par un champ dans ce DMV qui ne fait pas partie de la requête dans la question execution_count, qui montre encore une fois qu'il s'agit de données cumulatives. Et il est assez pratique d'avoir ces données cumulatives car vous pouvez obtenir des moyennes, etc. en divisant certaines des mesures par le execution_count.

La deuxième phrase, "les plans supprimés du cache sont également supprimés de ce DMV", indique que ce n'est pas du tout une image complète, surtout si le serveur a déjà un cache de plan assez complet et est en charge et donc expire les plans assez fréquemment. De plus, la plupart des DMV sont réinitialisés lorsque le serveur est réinitialisé, ils ne constituent donc pas un véritable historique, même si ces lignes n'ont pas été supprimées à l'expiration des plans.

Maintenant, contrastons ce qui précède avec sys.sysprocesses. Cette vue système montre uniquement ce qui est en cours d'exécution, tout comme la combinaison de sys.dm_exec_connections , sys.dm_exec_sessions et sys.dm_exec_requests (qui est indiquée sur la page liée pour sys.dm_exec_sessions). Il s'agit d'une vue entièrement différente du serveur par rapport à lasys.dm_exec_query_stats DMV qui contient les données même après la fin du processus. Signification, par rapport à la "les résultats de la deuxième requête sont-ils faux?" question, ils ne se trompent pas, ils se rapportent simplement à un aspect différent (c'est-à-dire le calendrier) des statistiques de performance.

Ainsi, la requête utilisant sys.sysprocessesne regarde que "en ce moment". Et la requête utilisant sys.dm_exec_query_statsregarde principalement (peut-être) ce qui s'est passé depuis le dernier redémarrage du service SQL Server (ou bien évidemment le redémarrage du système). Pour l'analyse générale des performances, il semble que sys.dm_exec_query_statsc'est beaucoup mieux, mais encore une fois, il laisse des informations utiles tout le temps. Et, dans les deux cas, vous devez également prendre en compte les points soulevés par @Aaron dans les commentaires de la question (depuis supprimés) concernant la précision de la valeur "database_id" en premier lieu (c'est-à-dire qu'elle ne reflète que la base de données active qui a initié le code , pas nécessairement là où le «problème» se produit).

Mais, si vous avez juste besoin / voulez avoir une idée de ce qui se passe en ce moment dans toutes les bases de données, peut - être parce que les choses ralentissent maintenant, vous feriez mieux d'utiliser la combinaison sys.dm_exec_connections, sys.dm_exec_sessionset sys.dm_exec_requests(et non dépréciée sys.sysprocesses). Gardez à l'esprit que vous recherchez / pour des requêtes , pas des bases de données , car les requêtes peuvent se joindre à plusieurs bases de données, inclure des UDF provenant d'une ou plusieurs bases de données, etc.


EDIT:
Si la préoccupation globale est de réduire les consommateurs de CPU élevés, recherchez les requêtes qui prennent le plus de CPU, car les bases de données n'utilisent pas réellement de CPU (la recherche par base de données peut fonctionner dans une société d'hébergement où chaque base de données est isolée et appartenant à un autre client).

La requête suivante aidera à identifier les requêtes avec une utilisation moyenne élevée du processeur. Il condense les données dans le DMV query_stats car ces enregistrements peuvent afficher plusieurs fois la même requête (oui, le même sous-ensemble du lot de requêtes), chacune avec un plan d'exécution différent.

;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
la source
est AvgCPUen millisecondes?
Kolob Canyon
Salut @KolobCanyon. Selon la documentation de sys.dm_exec_query_stats , il total_worker_times'agit de "la quantité totale de temps CPU, signalée en microsecondes (mais uniquement précise en millisecondes), qui a été consommée par les exécutions de ce plan depuis sa compilation. ". Est ce que ça aide? Cela pourrait facilement être converti en millisecondes si c'est ce que vous voulez voir.
Solomon Rutzky
1

J'ai ajusté la requête pour la division par 0 erreur et optimisé les noms de colonne pour copier / coller vers 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);
HakanM
la source
0

J'ai tellement aimé la requête CPU sys.dm_exec_query_statsque je l'ai étendue. Il est toujours classé par CPU mais j'ai ajouté d'autres totaux et pourcentages pour obtenir un meilleur profil de serveur. Cela copie bien dans Excel et avec une mise en forme conditionnelle des couleurs dans les colonnes Pourcentage, les pires chiffres ressortent bien. J'ai utilisé la «Graded Color Scale» avec 3 couleurs; une couleur rose pour les valeurs élevées, jaune pour le milieu, vert pour les basses.

J'ai ajouté une étiquette pour database id 32676laquelle est la base de données de ressources SQL interne. Je convertis le CPU et la durée en heures pour avoir une meilleure idée de l'utilisation du temps.

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
la source