Как получить загрузку процессора по базе данных для конкретного экземпляра?

15

Я обнаружил следующие запросы для определения использования ЦП базой данных, но они показывают разные результаты:

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%).

И запрос ниже говорит о том, что проблема связана с базой данных master и дистрибутива (около 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это устарело. Значит ли это, что результаты второго запроса неверны?

gotqn
источник

Ответы:

14

Хотя я, как и @Thomas, полностью согласен с @Aaron в комментариях к вопросу, касающемуся вопроса о том, является ли использование ЦП для каждой базы данных точным или полезным, я могу, по крайней мере, ответить на вопрос, почему эти два запроса являются такими разные. И причина, по которой они различаются, укажет, какой из них более точный, хотя этот более высокий уровень точности все еще относительно того, который является конкретно неточным, а значит, и не совсем точным ;-).

Первый запрос использует sys.dm_exec_query_stats для получения информации о процессоре (т.е. total_worker_time). Если вы перейдете на связанную страницу, которая является документацией MSDN для этого DMV, вы увидите краткое введение из 3 предложений и 2 из этих предложений, которые дают нам большую часть того, что нам нужно для понимания контекста этой информации («насколько она надежна») и "как это сравнить с sys.sysprocesses"). Эти два предложения:

Возвращает статистику общей производительности для кэшированных планов запросов в SQL Server. ... Когда план удаляется из кэша, соответствующие строки удаляются из этого представления

Первое предложение «Возвращает статистику производительности агрегата » говорит нам, что информация в этом DMV (как и в некоторых других) является накопительной и не относится только к тем запросам, которые выполняются в данный момент. На это также указывает поле в этом DMV, которое не является частью запроса в Вопросе execution_count, что снова показывает, что это совокупные данные. И очень удобно, чтобы эти данные были кумулятивными, так как вы можете получить средние значения и т. Д. Путем деления некоторых показателей на execution_count.

Второе предложение, «планы, удаляемые из кэша, также удаляются из этого DMV», указывают на то, что это не полная картина вообще, особенно если сервер уже имеет довольно полный кэш планов и находится под нагрузкой и, следовательно, истекает срок действия планов довольно часто. Кроме того, большинство DMV сбрасывается при перезагрузке сервера, поэтому они не являются истинной историей, даже если эти строки не были удалены при истечении срока действия планов.

Теперь давайте сопоставим с sys.sysprocesses. Это системное представление показывает только то, что в данный момент выполняется, точно так же, как сочетание sys.dm_exec_connections , sys.dm_exec_sessions и sys.dm_exec_requests (что указано на связанной странице для sys.dm_exec_sessions). Это совершенно другой взгляд на сервер по сравнению с sys.dm_exec_query_statsDMV, в котором хранятся данные даже после завершения процесса. Это означает, что «результаты второго запроса неверны?» вопрос, они не ошибаются, они просто относятся к другому аспекту (то есть временные рамки) статистики производительности.

Таким образом, запрос с использованием sys.sysprocessesтолько смотрит "прямо сейчас". И в запросе используется sys.dm_exec_query_statsв основном (возможно) то, что произошло с момента последнего перезапуска службы SQL Server (или, очевидно, перезагрузки системы). Для общего анализа производительности кажется, что sys.dm_exec_query_statsон намного лучше, но опять же, он постоянно сбрасывает полезную информацию. И в обоих случаях вам также необходимо учитывать замечания, сделанные @Aaron в комментариях к вопросу (поскольку они удалены), в первую очередь касающиеся точности значения «database_id» (т. Е. Оно отражает только активную БД, инициировавшую код не обязательно, где "проблема" происходит).

Но, если вам просто нужно / хочу , чтобы получить представление о том , что происходит прямо сейчас во всех базах данных, возможно , потому , что вещи замедляются прямо сейчас, вы лучше использовать комбинацию из sys.dm_exec_connections, sys.dm_exec_sessionsи sys.dm_exec_requests(и не рекомендуется sys.sysprocesses). Просто имейте в виду, что вы просматриваете / ищите запросы , а не базы данных , потому что запросы могут объединяться в несколько баз данных, включая пользовательские функции из одной или нескольких баз данных и т. Д.


РЕДАКТИРОВАТЬ:
если общая проблема заключается в сокращении нагрузки на ЦП, то ищите запросы, которые занимают больше всего ЦП, потому что базы данных фактически не занимают ЦП (поиск по базе данных может работать в хостинговой компании, где каждая база данных изолирована и принадлежит другому клиенту).

Следующий запрос поможет идентифицировать запросы с высокой средней загрузкой ЦП. Он конденсирует данные в DMV query_stats, поскольку эти записи могут показывать один и тот же запрос (да, одно и то же подмножество пакета запроса) несколько раз, каждый с разным планом выполнения.

;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;
Соломон Руцкий
источник
находится AvgCPUв миллисекундах?
Каньон Колоб
Привет @KolobCanyon. Согласно документации для sys.dm_exec_query_stats , total_worker_time« Общее количество процессорного времени, сообщаемое в микросекундах (но с точностью до миллисекунд), было использовано при выполнении этого плана с момента его компиляции ». Это помогает? Это может быть легко конвертировано в миллисекунды, если вы хотите видеть это.
Соломон Руцкий
1

Я настроил запрос на деление на 0 ошибок и оптимизировал имена столбцов для копирования / вставки в 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
источник
0

Мне sys.dm_exec_query_statsнастолько понравился запрос процессора, что я его расширил. Он все еще ранжируется по процессору, но я добавил другие итоги и проценты, чтобы получить лучший профиль сервера. Это прекрасно копирует в Excel и с условным форматированием цвета в столбцах Процент, худшие цифры хорошо выделяются. Я использовал «Градуированную цветовую шкалу» с 3 цветами; розовый цвет для высоких значений, желтый для среднего, зеленый для низких.

Я добавил метку, для database id 32676которой используется внутренняя база данных ресурсов SQL. Я конвертирую время процессора и длительности в часы, чтобы получить лучшее представление об использовании времени.

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);
Дрю Нефф
источник