Saya sedang melihat artikel di sini Tabel Sementara vs Variabel Tabel dan Efeknya pada Kinerja SQL Server dan pada SQL Server 2008 mampu mereproduksi hasil yang serupa dengan yang ditunjukkan di sana untuk tahun 2005.
Saat menjalankan prosedur tersimpan (definisi di bawah) dengan hanya 10 baris versi variabel tabel keluar melakukan versi tabel sementara lebih dari dua kali.
Saya membersihkan cache prosedur dan menjalankan kedua prosedur yang tersimpan 10.000 kali kemudian mengulangi proses untuk 4 berjalan lagi. Hasil di bawah (waktu dalam ms per batch)
T2_Time V2_Time
----------- -----------
8578 2718
6641 2781
6469 2813
6766 2797
6156 2719
Pertanyaan saya adalah: Apa alasan untuk kinerja versi variabel tabel yang lebih baik?
Saya sudah melakukan investigasi. mis. Melihat penghitung kinerja dengan
SELECT cntr_value
from sys.dm_os_performance_counters
where counter_name = 'Temp Tables Creation Rate';
menegaskan bahwa dalam kedua kasus objek sementara sedang di-cache setelah dijalankan pertama seperti yang diharapkan daripada dibuat dari awal lagi untuk setiap doa.
Demikian pula menelusuri Auto Stats
, SP:Recompile
, SQL:StmtRecompile
peristiwa di Profiler (gambar di bawah) menunjukkan bahwa peristiwa ini hanya terjadi sekali (pada doa pertama dari #temp
tabel disimpan prosedur) dan 9.999 eksekusi lain tidak menaikkan salah satu peristiwa ini. (Versi variabel tabel tidak mendapatkan salah satu dari peristiwa ini)
Overhead yang sedikit lebih besar dari proses pertama dari prosedur tersimpan tidak dapat menjelaskan perbedaan keseluruhan yang besar namun karena masih hanya membutuhkan beberapa ms untuk menghapus cache prosedur dan menjalankan kedua prosedur sekali jadi saya tidak percaya statistik atau kompilasi ulang bisa menjadi penyebabnya.
Buat Objek Database yang Diperlukan
CREATE DATABASE TESTDB_18Feb2012;
GO
USE TESTDB_18Feb2012;
CREATE TABLE NUM
(
n INT PRIMARY KEY,
s VARCHAR(128)
);
WITH NUMS(N)
AS (SELECT TOP 1000000 ROW_NUMBER() OVER (ORDER BY $/0)
FROM master..spt_values v1,
master..spt_values v2)
INSERT INTO NUM
SELECT N,
'Value: ' + CONVERT(VARCHAR, N)
FROM NUMS
GO
CREATE PROCEDURE [dbo].[T2] @total INT
AS
CREATE TABLE #T
(
n INT PRIMARY KEY,
s VARCHAR(128)
)
INSERT INTO #T
SELECT n,
s
FROM NUM
WHERE n%100 > 0
AND n <= @total
DECLARE @res VARCHAR(128)
SELECT @res = MAX(s)
FROM NUM
WHERE n <= @total
AND NOT EXISTS(SELECT *
FROM #T
WHERE #T.n = NUM.n)
GO
CREATE PROCEDURE [dbo].[V2] @total INT
AS
DECLARE @V TABLE (
n INT PRIMARY KEY,
s VARCHAR(128))
INSERT INTO @V
SELECT n,
s
FROM NUM
WHERE n%100 > 0
AND n <= @total
DECLARE @res VARCHAR(128)
SELECT @res = MAX(s)
FROM NUM
WHERE n <= @total
AND NOT EXISTS(SELECT *
FROM @V V
WHERE V.n = NUM.n)
GO
Skrip Tes
SET NOCOUNT ON;
DECLARE @T1 DATETIME2,
@T2 DATETIME2,
@T3 DATETIME2,
@Counter INT = 0
SET @T1 = SYSDATETIME()
WHILE ( @Counter < 10000)
BEGIN
EXEC dbo.T2 10
SET @Counter += 1
END
SET @T2 = SYSDATETIME()
SET @Counter = 0
WHILE ( @Counter < 10000)
BEGIN
EXEC dbo.V2 10
SET @Counter += 1
END
SET @T3 = SYSDATETIME()
SELECT DATEDIFF(MILLISECOND,@T1,@T2) AS T2_Time,
DATEDIFF(MILLISECOND,@T2,@T3) AS V2_Time
sumber
#temp
meja satu kali meskipun itu dibersihkan dan dihuni kembali sebanyak 9999 kali berikutnya.Jawaban:
Output
SET STATISTICS IO ON
untuk keduanya terlihat serupaMemberi
Dan seperti yang ditunjukkan Harun di komentar rencana untuk versi tabel variabel sebenarnya kurang efisien karena sementara keduanya memiliki loop bersarang berencana didorong oleh indeks mencari di
dbo.NUM
dalam#temp
Melakukan versi meja mencari ke dalam indeks pada[#T].n = [dbo].[NUM].[n]
dengan predikat sisa[#T].[n]<=[@total]
sedangkan variabel tabel versi melakukan pencarian indeks@V.n <= [@total]
dengan sisa predikat@V.[n]=[dbo].[NUM].[n]
dan memproses lebih banyak baris (itulah sebabnya rencana ini berkinerja sangat buruk untuk jumlah baris yang lebih besar)Menggunakan Extended Events untuk melihat jenis tunggu untuk spid spesifik memberikan hasil ini untuk 10.000 eksekusi
EXEC dbo.T2 10
dan hasil ini untuk 10.000 eksekusi
EXEC dbo.V2 10
Jadi jelas bahwa jumlah
PAGELATCH_SH
menunggu jauh lebih tinggi dalam#temp
case table. Saya tidak mengetahui adanya cara untuk menambahkan sumber daya tunggu ke jejak peristiwa yang diperluas sehingga untuk menyelidiki ini saya lanjutkanSementara di polling koneksi lain
sys.dm_os_waiting_tasks
Setelah meninggalkan menjalankan itu selama sekitar 15 detik itu telah mengumpulkan hasil sebagai berikut
Kedua halaman ini yang diikat milik (berbeda) indeks tidak berkerumun di
tempdb.sys.sysschobjs
tabel dasar bernama'nc1'
dan'nc2'
.Permintaan
tempdb.sys.fn_dblog
selama proses menunjukkan bahwa jumlah catatan log yang ditambahkan oleh eksekusi pertama dari setiap prosedur yang disimpan agak bervariasi tetapi untuk eksekusi berikutnya jumlah yang ditambahkan oleh setiap iterasi sangat konsisten dan dapat diprediksi. Setelah rencana prosedur di-cache, jumlah entri log adalah sekitar setengah dari yang dibutuhkan untuk#temp
versi.Melihat entri log transaksi secara lebih terperinci untuk
#temp
versi tabel SP, setiap pemanggilan berikutnya dari prosedur tersimpan menciptakan tiga transaksi dan satu variabel tabel hanya dua.The
INSERT
/TVQUERY
transaksi adalah identik kecuali untuk nama. Ini berisi catatan log untuk masing-masing dari 10 baris yang dimasukkan ke tabel sementara atau variabel tabel ditambah entriLOP_BEGIN_XACT
/LOP_COMMIT_XACT
.The
CREATE TABLE
transaksi hanya muncul dalam#Temp
versi dan terlihat sebagai berikut.The
FCheckAndCleanupCachedTempTable
transaksi muncul baik tetapi memiliki 6 entri tambahan di#temp
versi. Ini adalah 6 baris yang merujuksys.sysschobjs
dan mereka memiliki pola yang persis sama seperti di atas.Melihat 6 baris dalam kedua transaksi ini, mereka berhubungan dengan operasi yang sama. Yang pertama
LOP_MODIFY_ROW, LCX_CLUSTERED
adalah pembaruan kemodify_date
kolom disys.objects
. Lima baris yang tersisa semuanya berkaitan dengan penggantian nama objek. Karenaname
merupakan kolom kunci dari kedua NCI yang terkena dampak (nc1
dannc2
) ini dilakukan sebagai delete / insert untuk yang kemudian kembali ke indeks berkerumun dan memperbarui itu juga.Tampaknya untuk
#temp
versi tabel ketika prosedur tersimpan mengakhiri bagian dari pembersihan yang dilakukan olehFCheckAndCleanupCachedTempTable
transaksi adalah mengubah nama tabel temp dari sesuatu seperti#T__________________________________________________________________________________________________________________00000000E316
menjadi nama internal yang berbeda seperti#2F4A0079
dan ketika dimasukkan,CREATE TABLE
transaksi mengubah nama kembali. Nama flip flopping ini dapat dilihat dengan di satu koneksi mengeksekusidbo.T2
dalam satu loop sementara yang lainContoh Hasil
Jadi salah satu penjelasan potensial untuk perbedaan kinerja yang diamati seperti yang disinggung oleh Alex adalah bahwa pekerjaan tambahan ini menjaga tabel sistem
tempdb
yang bertanggung jawab.Menjalankan kedua prosedur dalam satu lingkaran, profiler Visual Studio Code mengungkapkan yang berikut ini
Versi variabel tabel menghabiskan sekitar 60% dari waktu melakukan pernyataan penyisipan dan pemilihan berikutnya sedangkan tabel sementara kurang dari setengahnya. Ini sejalan dengan waktu yang ditunjukkan dalam OP dan dengan kesimpulan di atas bahwa perbedaan kinerja adalah waktu yang dihabiskan untuk melakukan pekerjaan tambahan bukan karena waktu yang dihabiskan dalam eksekusi permintaan itu sendiri.
Fungsi terpenting yang berkontribusi terhadap 75% yang "hilang" dalam versi tabel sementara adalah
Di bawah kedua fungsi buat dan lepas fungsi
CMEDProxyObject::SetName
tersebut ditampilkan dengan nilai sampel inklusif dari19.6%
. Dari yang saya simpulkan bahwa 39,2% dari waktu dalam kasus tabel sementara diambil dengan penggantian nama yang dijelaskan sebelumnya.Dan yang terbesar dalam versi variabel tabel berkontribusi untuk 40% lainnya
Profil Tabel Sementara
Profil Variabel Tabel
sumber
Disko Inferno
Karena ini adalah pertanyaan yang lebih lama, saya memutuskan untuk meninjau kembali masalah pada versi SQL Server yang lebih baru untuk melihat apakah profil kinerja yang sama masih ada, atau apakah karakteristiknya telah berubah sama sekali.
Secara khusus, penambahan tabel sistem dalam-memori untuk SQL Server 2019 tampaknya merupakan kesempatan yang berharga untuk pengujian ulang.
Saya menggunakan test harness yang sedikit berbeda, karena saya mengalami masalah ini saat mengerjakan sesuatu yang lain.
Pengujian, pengujian
Menggunakan Stack Overflow versi 2013 , saya memiliki indeks ini dan dua prosedur ini:
Indeks:
Meja temp:
Variabel tabel:
Untuk mencegah kemungkinan menunggu ASYNC_NETWORK_IO , saya menggunakan prosedur pembungkus.
SQL Server 2017
Sejak 2014 dan 2016 pada dasarnya RELICS pada titik ini, saya memulai pengujian saya dengan 2017. Juga, untuk singkatnya, saya langsung melompat ke profiling kode dengan Perfview . Dalam kehidupan nyata, saya melihat menunggu, kait, spinlocks, bendera jejak gila, dan hal-hal lainnya.
Membuat profil kode adalah satu-satunya hal yang mengungkapkan minat.
Perbedaan waktu:
Masih perbedaan yang sangat jelas, eh? Tapi apa yang memukul SQL Server sekarang?
Melihat dua peningkatan teratas dalam sampel diffed, kita melihat
sqlmin
dansqlsqllang!TCacheStore<CacheClockAlgorithm>::GetNextUserDataInHashBucket
merupakan dua pelanggar terbesar.Dilihat oleh nama-nama dalam tumpukan panggilan, membersihkan dan secara internal mengubah nama tabel temp tampaknya menjadi waktu terbesar menyebalkan dalam panggilan tabel temp vs panggilan variabel tabel.
Meskipun variabel tabel didukung secara internal oleh temp tables, ini tampaknya tidak menjadi masalah.
Melihat melalui tumpukan panggilan untuk uji variabel tabel tidak menunjukkan salah satu dari pelaku utama sama sekali:
SQL Server 2019 (Vanilla)
Baiklah, jadi ini masih menjadi masalah di SQL Server 2017, apakah ada yang berbeda di 2019 di luar kotak?
Pertama, untuk menunjukkan tidak ada apa-apa di lengan saya:
Perbedaan waktu:
Kedua prosedur berbeda. Panggilan tabel temp adalah beberapa detik lebih cepat, dan panggilan variabel tabel sekitar 1,5 detik lebih lambat. Variabel tabel melambat sebagian dapat dijelaskan oleh kompilasi variabel tabel ditangguhkan , pilihan pengoptimal baru pada 2019.
Melihat perbedaan dalam Perfview, itu telah berubah sedikit - sqlmin sudah tidak ada lagi - tetapi
sqllang!TCacheStore<CacheClockAlgorithm>::GetNextUserDataInHashBucket
ada.SQL Server 2019 (tabel sistem In-Memory Tempdb)
Bagaimana dengan ini baru dalam hal tabel sistem memori? Hm? Sup dengan itu?
Ayo nyalakan!
Perhatikan bahwa ini membutuhkan restart Server SQL untuk memulai, jadi maafkan saya ketika saya reboot SQL pada Jumat sore yang indah ini.
Sekarang semuanya terlihat berbeda:
Perbedaan waktu:
Tabel temp melakukan sekitar 4 detik lebih baik! Itu sesuatu.
Saya suka sesuatu.
Kali ini, perbedaan Perfview tidak terlalu menarik. Berdampingan, menarik untuk dicatat seberapa dekat waktu di papan:
Satu hal yang menarik di diff adalah panggilan ke
hkengine!
, yang mungkin tampak jelas karena fitur hekaton-ish sekarang digunakan.Sejauh dua item teratas dalam diff, saya tidak dapat membuat banyak dari
ntoskrnl!?
:Atau
sqltses!CSqlSortManager_80::GetSortKey
, tetapi mereka ada di sini untuk Smrtr Ppl ™ untuk melihat:Perhatikan bahwa ada yang tidak terdokumentasi dan jelas tidak aman untuk produksi, jadi jangan gunakan bendera jejak startup yang dapat Anda gunakan untuk memiliki objek sistem tabel temp tambahan (sysrowsets, sysallocunits, dan sysseobjvalues) yang termasuk dalam fitur di dalam memori, tetapi tidak membuat perbedaan nyata dalam waktu eksekusi dalam kasus ini.
Pembulatan
Bahkan dalam versi SQL Server yang lebih baru, panggilan frekuensi tinggi ke variabel tabel jauh lebih cepat daripada panggilan frekuensi tinggi ke tabel temp.
Meskipun tergoda untuk menyalahkan kompilasi, kompilasi ulang, statistik otomatis, kait, spinlocks, caching, atau masalah lainnya, masalah ini jelas masih seputar pengelolaan pembersihan tabel temp.
Ini panggilan yang lebih dekat di SQL Server 2019 dengan tabel sistem di-memori diaktifkan, tetapi variabel tabel masih berkinerja lebih baik ketika frekuensi panggilan tinggi.
Tentu saja, sebagai orang bijak vaping sekali renung: "gunakan variabel tabel ketika pilihan rencana tidak menjadi masalah".
sumber