Sqlserver
 sql >> Teknologi Basis Data >  >> RDS >> Sqlserver

SQL Server 2016 :sys.dm_exec_function_stats

Di SQL Server 2016 CTP 2.1, ada satu objek baru yang muncul setelah CTP 2.0:sys.dm_exec_function_stats. Ini dimaksudkan untuk menyediakan fungsionalitas yang mirip dengan sys.dm_exec_procedure_stats, sys.dm_exec_query_stats, dan sys.dm_exec_trigger_stats. Jadi sekarang mungkin untuk melacak metrik waktu proses agregat untuk fungsi yang ditentukan pengguna.

Atau ya?

Setidaknya di CTP 2.1, saya hanya bisa mendapatkan metrik yang berarti di sini untuk fungsi skalar biasa – tidak ada yang terdaftar untuk TVF inline atau multi-pernyataan. Saya tidak terkejut dengan fungsi inline, karena mereka pada dasarnya diperluas sebelum dieksekusi. Tetapi karena TVF multi-pernyataan sering kali menjadi masalah kinerja, saya berharap mereka juga muncul. Mereka masih muncul di sys.dm_exec_query_stats, jadi Anda masih dapat memperoleh metrik kinerjanya dari sana, tetapi mungkin sulit untuk melakukan agregasi ketika Anda benar-benar memiliki beberapa pernyataan yang melakukan beberapa bagian pekerjaan – tidak ada yang digulung untuk Anda.

Mari kita lihat sekilas bagaimana ini dimainkan. Katakanlah kita memiliki tabel sederhana dengan 100.000 baris:

SELECT TOP (100000) o1.[object_id], o1.create_date
  INTO dbo.src
  FROM sys.all_objects AS o1
  CROSS JOIN sys.all_objects AS o2
  ORDER BY o1.[object_id];
GO
CREATE CLUSTERED INDEX x ON dbo.src([object_id]);
GO
-- prime the cache
SELECT [object_id], create_date FROM dbo.src;

Saya ingin membandingkan apa yang terjadi ketika kami menyelidiki UDF skalar, fungsi bernilai tabel multi-pernyataan, dan fungsi bernilai tabel sebaris, dan bagaimana kami melihat pekerjaan apa yang dilakukan dalam setiap kasus. Pertama, bayangkan sesuatu yang sepele yang bisa kita lakukan di SELECT klausa, tetapi kita mungkin ingin membaginya, seperti memformat tanggal sebagai string:

CREATE PROCEDURE dbo.p_dt_Standard
  @dt_ CHAR(10) = NULL
AS
BEGIN
  SET NOCOUNT ON;
  SELECT @dt_ = CONVERT(CHAR(10), create_date, 120)
    FROM dbo.src
    ORDER BY [object_id];
END
GO

(Saya menetapkan output ke variabel, yang memaksa seluruh tabel untuk dipindai, tetapi mencegah metrik kinerja dipengaruhi oleh upaya SSMS untuk menggunakan dan merender output. Terima kasih atas pengingatnya, Mikael Eriksson.)

Sering kali Anda akan melihat orang memasukkan konversi itu ke dalam suatu fungsi, dan itu bisa berupa skalar atau TVF, seperti ini:

CREATE FUNCTION dbo.dt_Inline(@dt_ DATETIME)
RETURNS TABLE
AS
  RETURN (SELECT dt_ = CONVERT(CHAR(10), @dt_, 120));
GO
 
CREATE FUNCTION dbo.dt_Multi(@dt_ DATETIME)
RETURNS @t TABLE(dt_ CHAR(10))
AS
BEGIN
  INSERT @t(dt_) SELECT CONVERT(CHAR(10), @dt_, 120);
  RETURN;
END
GO
 
CREATE FUNCTION dbo.dt_Scalar(@dt_ DATETIME)
RETURNS CHAR(10)
AS
BEGIN
  RETURN (SELECT CONVERT(CHAR(10), @dt_, 120));
END
GO

Saya membuat pembungkus prosedur di sekitar fungsi-fungsi ini sebagai berikut:

CREATE PROCEDURE dbo.p_dt_Inline
  @dt_ CHAR(10) = NULL
AS
BEGIN
  SET NOCOUNT ON;
  SELECT @dt_ = dt.dt_
    FROM dbo.src AS o
    CROSS APPLY dbo.dt_Inline(o.create_date) AS dt
    ORDER BY o.[object_id];
END
GO
 
CREATE PROCEDURE dbo.p_dt_Multi
  @dt_ CHAR(10) = NULL
AS
BEGIN
  SET NOCOUNT ON;
  SELECT @dt_ = dt.dt_
    FROM dbo.src
    CROSS APPLY dbo.dt_Multi(create_date) AS dt
    ORDER BY [object_id];
END
GO
 
CREATE PROCEDURE dbo.p_dt_Scalar
  @dt_ CHAR(10) = NULL
AS
BEGIN
  SET NOCOUNT ON;
  SELECT @dt_ = dt = dbo.dt_Scalar(create_date)
    FROM dbo.src
    ORDER BY [object_id];
END
GO

(Dan tidak, dt_ konvensi yang Anda lihat bukanlah hal baru yang menurut saya adalah ide yang bagus, itu hanya cara paling sederhana saya dapat mengisolasi semua pertanyaan ini di DMV dari semua yang dikumpulkan. Itu juga memudahkan untuk menambahkan sufiks untuk membedakan dengan mudah antara kueri di dalam prosedur tersimpan dan versi ad hoc.)

Selanjutnya, saya membuat tabel #temp untuk menyimpan pengaturan waktu, dan mengulangi proses ini (keduanya menjalankan prosedur tersimpan dua kali, dan menjalankan isi prosedur sebagai kueri ad hoc terisolasi dua kali, dan melacak waktu masing-masing):

CREATE TABLE #t
(
  ID INT IDENTITY(1,1), 
  q VARCHAR(32), 
  s DATETIME2, 
  e DATETIME2
);
GO
 
INSERT #t(q,s) VALUES('p Standard',SYSDATETIME());
GO
 
EXEC dbo.p_dt_Standard;
GO 2
 
UPDATE #t SET e = SYSDATETIME() WHERE ID = 1;
GO
 
INSERT #t(q,s) VALUES('ad hoc Standard',SYSDATETIME());
GO
 
DECLARE @dt_st CHAR(10);
  SELECT @dt_st = CONVERT(CHAR(10), create_date, 120)
    FROM dbo.src
    ORDER BY [object_id];
GO 2
 
UPDATE #t SET e = SYSDATETIME() WHERE ID = 2;
GO
-- repeat for inline, multi and scalar versions

Kemudian saya menjalankan beberapa kueri diagnostik, dan inilah hasilnya:

sys.dm_exec_function_stats

SELECT name = OBJECT_NAME(object_id), 
  execution_count,
  time_milliseconds = total_elapsed_time/1000
FROM sys.dm_exec_function_stats
WHERE database_id = DB_ID()
ORDER BY name;

Hasil:

name        execution_count    time_milliseconds
---------   ---------------    -----------------
dt_Scalar   400000             1116

Itu bukan salah ketik; hanya UDF skalar yang menunjukkan keberadaan di DMV baru.

sys.dm_exec_procedure_stats

SELECT name = OBJECT_NAME(object_id), 
  execution_count,
  time_milliseconds = total_elapsed_time/1000
FROM sys.dm_exec_procedure_stats
WHERE database_id = DB_ID()
ORDER BY name;

Hasil:

name            execution_count    time_milliseconds
-------------   ---------------    -----------------
p_dt_Inline     2                  74
p_dt_Multi      2                  269
p_dt_Scalar     2                  1063
p_dt_Standard   2                  75

Ini bukan hasil yang mengejutkan:menggunakan fungsi skalar menyebabkan penalti kinerja urutan besaran, sedangkan TVF multi-pernyataan hanya sekitar 4x lebih buruk. Selama beberapa pengujian, fungsi inline selalu lebih cepat atau satu atau dua milidetik lebih cepat daripada tidak ada fungsi sama sekali.

sys.dm_exec_query_stats

SELECT 
  query = SUBSTRING([text],s,e), 
  execution_count, 
  time_milliseconds
FROM
(
  SELECT t.[text],
    s = s.statement_start_offset/2 + 1,
    e = COALESCE(NULLIF(s.statement_end_offset,-1),8000)/2,
    s.execution_count,
    time_milliseconds = s.total_elapsed_time/1000
  FROM sys.dm_exec_query_stats AS s
  OUTER APPLY sys.dm_exec_sql_text(s.[sql_handle]) AS t
  WHERE t.[text] LIKE N'%dt[_]%' 
) AS x;

Hasil terpotong, diurutkan ulang secara manual:

query (truncated)                                                       execution_count    time_milliseconds
--------------------------------------------------------------------    ---------------    -----------------
-- p Standard:
SELECT @dt_ = CONVERT(CHAR(10), create_date, 120) ...                   2                  75
-- ad hoc Standard:
SELECT @dt_st = CONVERT(CHAR(10), create_date, 120) ...                 2                  72
 
-- p Inline:
SELECT @dt_ = dt.dt_ FROM dbo.src AS o CROSS APPLY dbo.dt_Inline...     2                  74
-- ad hoc Inline:
SELECT @dt_in = dt.dt_ FROM dbo.src AS o CROSS APPLY dbo.dt_Inline...   2                  72
 
-- all Multi:
INSERT @t(dt_) SELECT CONVERT(CHAR(10), @dt_, 120);                     184                5
-- p Multi:
SELECT @dt_ = dt.dt_ FROM dbo.src CROSS APPLY dbo.dt_Multi...           2                  270
-- ad hoc Multi:
SELECT @dt_m = dt.dt_ FROM dbo.src AS o CROSS APPLY dbo.dt_Multi...     2                  257
 
-- all scalar:
RETURN (SELECT CONVERT(CHAR(10), @dt_, 120));                           400000             581
-- p Scalar:
SELECT @dt_ = dbo.dt_Scalar(create_date)...                             2                  986
-- ad hoc Scalar:
SELECT @dt_sc = dbo.dt_Scalar(create_date)...                           2                  902

Hal penting yang perlu diperhatikan di sini adalah bahwa waktu dalam milidetik untuk INSERT di TVF multi-pernyataan dan pernyataan RETURN dalam fungsi skalar juga diperhitungkan dalam SELECT individu, jadi tidak masuk akal untuk hanya menambahkan semua waktunya.

Pengaturan waktu manual

Dan akhirnya, pengaturan waktu dari tabel #temp:

SELECT query = q, 
    time_milliseconds = DATEDIFF(millisecond, s, e) 
  FROM #t 
  ORDER BY ID;

Hasil:

query             time_milliseconds
---------------   -----------------
p Standard        107
ad hoc Standard   78
p Inline          80
ad hoc Inline     78
p Multi           351
ad hoc Multi      263
p Scalar          992
ad hoc Scalar     907

Hasil menarik tambahan di sini:pembungkus prosedur selalu memiliki beberapa overhead, meskipun seberapa signifikan itu mungkin benar-benar subjektif.

Ringkasan

Maksud saya di sini hari ini hanyalah untuk menunjukkan DMV baru dalam tindakan, dan menetapkan harapan dengan benar – beberapa metrik kinerja untuk fungsi masih akan menyesatkan, dan beberapa masih tidak akan tersedia sama sekali (atau setidaknya sangat membosankan untuk dikumpulkan sendiri ).

Saya pikir DMV baru ini mencakup salah satu bagian terbesar dari pemantauan kueri yang sebelumnya tidak ada pada SQL Server:bahwa fungsi skalar terkadang merupakan pembunuh kinerja yang tidak terlihat, karena satu-satunya cara yang dapat diandalkan untuk mengidentifikasi penggunaannya adalah dengan mengurai teks kueri, yang jauh dari sangat mudah. Jangan pedulikan fakta bahwa itu tidak akan memungkinkan Anda untuk mengisolasi dampaknya terhadap kinerja, atau bahwa Anda harus mencari UDF skalar dalam teks kueri sejak awal.

Lampiran

Saya telah melampirkan skrip:DMExecFunctionStats.zip

Juga, pada CTP1, berikut adalah kumpulan kolom:

database_id object_id type type_desc
sql_handle plan_handle cached_time last_execution_time execution_count
total_worker_time last_worker_time min_worker_time max_worker_time
total_physical_reads last_physical_reads min_physical_reads max_physical_reads
total_logical_writes last_logical_writes min_logical_writes max_logical_writes
total_logical_reads last_logical_reads min_logical_reads max_logical_reads
total_elapsed_time last_elapsed_time min_elapsed_time max_elapsed_time

Kolom saat ini ada di sys.dm_exec_function_stats


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. C# Setara dengan Tipe Data SQL Server

  2. Bagaimana cara langsung mengeksekusi kueri SQL di C #?

  3. Kueri Select SUM saya mengembalikan nol. Itu harus mengembalikan 0

  4. Cara Menginstal SQL Server di Windows

  5. ORDER BY item harus muncul dalam daftar pilih jika SELECT DISTINCT ditentukan