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