Як отримати використання процесора в базі даних для конкретного випадку?


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

А запит, наведений нижче, говорить про те, що проблема полягає в головному і базі даних розподілу (близько 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це уточнено. Чи означає це, що результати другого запиту помилкові?

Відповіді:


14

Хоча я, як і @Thomas, повністю погоджуюся з @Aaron в коментарях до питання щодо того, що "використання CPU на базі даних" є точним або корисним, я можу принаймні відповісти на питання, чому ці два запити так інший. І причина, чому вони відрізняються, вкаже, хто з них точніший, хоча той рівень вищої точності все ще відносно того, який є конкретно неточним, отже, все ще не справді точним ;-).

Перший запит використовує 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_sesions та 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). Просто майте на увазі, що ви шукаєте / для запитів , а не баз даних , оскільки запити можуть об'єднуватися в декілька баз даних, включати UDF з однієї або декількох баз даних тощо.


EDIT:
Якщо загальне занепокоєння скорочує високих споживачів процесора, то шукайте запити, які займають найбільше CPU, оскільки бази даних насправді не займають процесор (дивлячись на одну базу даних, можливо, працює в хостинг-компанії, де кожна база даних ізольована і належить іншому замовнику).

Наступний запит допоможе визначити запити з високим середнім рівнем використання процесора. Це стискає дані в DMV запиту_статів, оскільки ці записи можуть показувати один і той же запит (так, однаковий підмножина пакету запитів) кілька разів, кожен з іншим планом виконання.

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

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);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.