web-dev-qa-db-fra.com

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

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é le sys.sysprocesses est décréé. Est-ce à dire que les résultats de la deuxième requête sont erronés?

15
gotqn

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 des informations sur le processeur (c'est-à-dire total_worker_time). Si vous allez à 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 à seulement les requêtes en cours d'exécution. Ceci est également indiqué par un champ dans ce DMV qui ne fait pas partie de la requête dans la question, execution_count, ce qui montre à nouveau qu'il s'agit de données cumulatives. Et il est très 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 cours de chargement 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 affiche uniquement ce qui est en cours d'exécution, tout comme la combinaison sys.dm_exec_connections , sys.dm_exec_sessions et sys.dm_exec_requests ( qui est indiqué sur la page liée pour sys.dm_exec_sessions). Il s'agit d'une vue entièrement différente du serveur par rapport à sys.dm_exec_query_stats DMV qui conserve 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.sysprocesses ne regarde que "en ce moment". Et la requête utilisant sys.dm_exec_query_stats regarde surtout (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_stats est bien mieux, mais encore une fois, il laisse tout le temps des informations utiles. 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 actuellement dans toutes les bases de données, peut-être parce que les choses ralentissent en ce moment, il vaut mieux utiliser la combinaison de sys.dm_exec_connections, sys.dm_exec_sessions, et sys.dm_exec_requests (et non la version obsolète sys.sysprocesses). Gardez à l'esprit que vous recherchez/pour requêtes, pas bases de données, car les requêtes peuvent se joindre à plusieurs bases de données, y compris les FDU d'une ou plusieurs bases de données, etc.


MODIFIER:
Si la préoccupation générale 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é et appartient à 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;
14
Solomon Rutzky

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

J'ai aimé la requête CPU de sys.dm_exec_query_stats tellement que je l'ai étendu. Il est toujours classé par CPU mais j'ai ajouté d'autres totaux et pourcentages pour obtenir un meilleur profil de serveur. Cela se 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é un libellé pour database id 32676 qui est la base de données de ressources SQL interne. Je convertis le temps 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);
0
Drew Neff