Bagaimana cara mendapatkan penggunaan CPU dengan basis data misalnya?

15

Saya telah menemukan pertanyaan berikut untuk mendeteksi penggunaan CPU oleh database, tetapi mereka menunjukkan hasil yang berbeda:

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

Permintaan di atas memberi tahu bahwa masalahnya ada pada salah satu basis data saya (hampir 96%).

Dan pertanyaan di bawah ini memberi tahu bahwa masalahnya ada pada master dan basis data distribusi (sekitar 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

Saya telah memeriksa apakah itu sys.sysprocessessudah dihilangkan. Apakah ini berarti bahwa hasil dari kueri kedua salah?

Gotqn
sumber

Jawaban:

14

Sementara saya, seperti @Thomas, sepenuhnya setuju dengan @ Harun dalam komentar tentang pertanyaan tentang "penggunaan CPU per-basis data" menjadi akurat atau berguna, saya setidaknya bisa menjawab pertanyaan mengapa kedua pertanyaan itu sangat berbeda. Dan alasan mengapa mereka berbeda akan menunjukkan mana yang lebih akurat, meskipun tingkat akurasi yang lebih tinggi masih relatif terhadap yang secara spesifik tidak akurat, karenanya masih belum benar-benar akurat ;-).

Kueri pertama menggunakan sys.dm_exec_query_stats untuk mendapatkan info CPU (yaitu total_worker_time). Jika Anda pergi ke halaman tertaut yang merupakan dokumentasi MSDN untuk DMV itu, Anda akan melihat intro pendek 3 kalimat dan 2 kalimat tersebut memberi kita sebagian besar dari apa yang perlu kita pahami konteks info ini ("seberapa andal itu" dan "bagaimana perbandingannya dengan sys.sysprocesses"). Dua kalimat itu adalah:

Mengembalikan statistik kinerja gabungan untuk paket kueri yang di-cache di SQL Server. ... Ketika sebuah rencana dihapus dari cache, baris yang sesuai dihilangkan dari pandangan ini

Kalimat pertama, "Pengembalian statistik kinerja agregat ", memberi tahu kami bahwa informasi dalam DMV ini (seperti beberapa yang lain) bersifat kumulatif dan tidak khusus untuk hanya kueri yang sedang berjalan. Ini juga ditunjukkan oleh bidang dalam DMV yang bukan bagian dari kueri dalam Pertanyaan execution_count, yang lagi-lagi menunjukkan bahwa ini adalah data kumulatif. Dan sangat berguna untuk membuat data ini menjadi kumulatif karena Anda bisa mendapatkan rata-rata, dll dengan membagi beberapa metrik dengan execution_count.

Kalimat kedua, "paket yang dihapus dari cache juga dihapus dari DMV ini", menunjukkan bahwa itu bukan gambar yang lengkap sama sekali, terutama jika server sudah memiliki cache paket yang cukup lengkap dan sedang dimuat dan karenanya paket yang kedaluwarsa agak sering. Juga, sebagian besar DMV di-reset ketika server me-reset sehingga mereka bukan sejarah yang sebenarnya bahkan jika baris ini tidak dihapus ketika paket berakhir.

Sekarang mari kita kontraskan di atas dengan sys.sysprocesses. Tampilan sistem ini hanya menampilkan apa yang sedang berjalan, seperti kombinasi sys.dm_exec_connections , sys.dm_exec_sessions , dan sys.dm_exec_requests (yang dinyatakan pada halaman yang terhubung untuk sys.dm_exec_sessions). Ini adalah pandangan yang sama sekali berbeda dari server dibandingkan dengan sys.dm_exec_query_statsDMV yang menyimpan data bahkan setelah proses selesai. Artinya, terkait dengan "apakah hasil dari kueri kedua salah?" pertanyaan, mereka tidak salah, mereka hanya berkaitan dengan aspek yang berbeda (yaitu kerangka waktu) dari statistik kinerja.

Jadi, permintaan menggunakan sys.sysprocesseshanya melihat "sekarang". Dan query menggunakan sys.dm_exec_query_statsadalah melihat sebagian besar (mungkin) apa yang telah terjadi sejak terakhir restart layanan SQL Server (atau jelas sistem reboot). Untuk analisis kinerja umum, tampaknya itu sys.dm_exec_query_statsjauh lebih baik, tetapi sekali lagi, ia menjatuhkan informasi yang bermanfaat setiap saat. Dan, dalam kedua kasus, Anda juga perlu mempertimbangkan poin yang dibuat oleh @ Harun dalam komentar pertanyaan (sejak dihapus) mengenai keakuratan nilai "database_id" di tempat pertama (yaitu hanya mencerminkan DB aktif yang memprakarsai kode , belum tentu di mana "masalah" terjadi).

Tapi, jika Anda hanya perlu / ingin mendapatkan rasa apa yang terjadi sekarang di semua Database, mungkin karena hal-hal yang melambat sekarang, Anda lebih baik menggunakan kombinasi dari sys.dm_exec_connections, sys.dm_exec_sessions, dan sys.dm_exec_requests(dan bukan usang sys.sysprocesses). Perlu diingat bahwa Anda melihat / untuk permintaan , bukan basis data , karena permintaan dapat bergabung di banyak basis data, termasuk UDF dari satu atau lebih basis data, dll.


EDIT:
Jika kekhawatiran keseluruhan adalah mengurangi konsumen CPU yang tinggi, maka cari pertanyaan yang mengambil paling banyak CPU, karena database tidak benar-benar mengambil CPU (mencari per database mungkin bekerja di perusahaan hosting di mana setiap database terisolasi dan dimiliki oleh pelanggan yang berbeda).

Kueri berikut akan membantu mengidentifikasi kueri dengan penggunaan CPU rata-rata tinggi. Itu mengembun data dalam DM_ query_stats karena catatan tersebut dapat menampilkan kueri yang sama (ya, bagian yang sama dari kumpulan kueri) beberapa kali, masing-masing dengan rencana eksekusi yang berbeda.

;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
sumber
adalah AvgCPUdalam milidetik?
Kolob Canyon
Hai @KolobCanyon. Menurut dokumentasi untuk sys.dm_exec_query_stats , total_worker_timeadalah " Jumlah total waktu CPU, dilaporkan dalam mikrodetik (tetapi hanya akurat hingga milidetik), yang dikonsumsi oleh eksekusi rencana ini sejak dikompilasi. ". Apakah itu membantu? Itu bisa dengan mudah dikonversi menjadi milidetik jika itu yang Anda inginkan.
Solomon Rutzky
1

Saya telah menyesuaikan kueri untuk pembagian dengan 0 kesalahan dan nama kolom yang dioptimalkan untuk salin / tempel ke 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
sumber
0

Saya menyukai permintaan CPU dari sys.dm_exec_query_statsbegitu banyak sehingga saya memperpanjangnya. Ini masih diperingkat oleh CPU tetapi saya menambahkan total dan persen lainnya untuk mendapatkan profil server yang lebih baik. Ini menyalin dengan baik ke Excel dan dengan pemformatan warna bersyarat pada kolom Persen, angka terburuk menonjol dengan baik. Saya menggunakan 'Skala Warna Bergradasi' dengan 3 warna; warna mawar untuk nilai tinggi, kuning untuk menengah, hijau untuk rendah.

Saya menambahkan Label database id 32676yang merupakan Database SQL Resource Internal. Saya mengonversi waktu CPU dan Durasi menjadi Jam untuk mendapatkan rasa penggunaan waktu yang lebih baik.

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
sumber