Prosedur yang disimpan lambat saat dipanggil dari web, cepat dari Management Studio

97

Saya telah menyimpan prosedur yang secara gila-gilaan setiap kali dipanggil dari aplikasi web.

Saya menyalakan Sql Profiler dan menelusuri panggilan waktu itu dan akhirnya menemukan hal-hal ini:

  1. Ketika menjalankan pernyataan dari dalam MS SQL Management Studio, dengan argumen yang sama (pada kenyataannya, saya menyalin panggilan prosedur dari jejak profil sql dan menjalankannya): Ini selesai dalam rata-rata 5 ~ 6 detik.
  2. Tetapi ketika dipanggil dari aplikasi web, dibutuhkan lebih dari 30 detik (dalam jejak) sehingga halaman web saya benar-benar habis waktu itu.

Terlepas dari kenyataan bahwa aplikasi web saya memiliki pengguna sendiri, semuanya sama (database, koneksi, server yang sama, dll.) Saya juga mencoba menjalankan kueri langsung di studio dengan pengguna aplikasi web dan tidak memakan waktu lebih dari 6 detik.

Bagaimana cara mengetahui apa yang terjadi?

Saya berasumsi itu tidak ada hubungannya dengan fakta bahwa kami menggunakan BLL> DAL layers atau adaptor Tabel karena jejaknya dengan jelas menunjukkan penundaan dalam prosedur sebenarnya. Hanya itu yang bisa saya pikirkan.

EDIT Saya menemukan di tautan ini bahwa ADO.NET disetel ARITHABORTke true - yang baik untuk sebagian besar waktu tetapi kadang-kadang ini terjadi, dan solusi yang disarankan adalah menambahkan with recompileopsi ke proc yang disimpan. Dalam kasus saya, itu tidak berfungsi tetapi saya curiga itu adalah sesuatu yang sangat mirip dengan ini. Ada yang tahu apa lagi yang dilakukan ADO.NET atau di mana saya dapat menemukan spesifikasinya?

saya serius
sumber
1
Ini mungkin terkait dengan berapa banyak data yang dikembalikan?
Barry Kaye
@ Barry: Tidak, karena saya menjalankan prosedur yang sama (disalin dari jejak juga - artinya parameter yang sama) di studio manajemen, ini berjalan dalam 6 detik.
iamserious
@Jayantha: Intinya BUKAN bahwa spnya lambat, tapi SESUATU antara ado.net dan sql adalah. Saya tidak melihat bagaimana sp akan membuat perbedaan.
iamserious
2
Apakah SP mengembalikan banyak data, misalnya kolom image / text / varchar (max)? Jumlah data yang akan dikonsumsi pada klien akan sangat besar yang akan memakan banyak waktu. SSMS memotong kumpulan hasil ini dengan cara yang lebih efisien.
Tim Schmelter
1
Kemungkinan duplikat stackoverflow.com/questions/2248112/…
Aaron Bertrand

Jawaban:

79

Saya pernah mengalami masalah serupa di masa lalu, jadi saya ingin melihat resolusi untuk pertanyaan ini. Komentar Aaron Bertrand pada OP menyebabkan Query time out ketika dieksekusi dari web, tetapi super cepat ketika dieksekusi dari SSMS , dan meskipun pertanyaannya bukan duplikat, jawabannya mungkin berlaku untuk situasi Anda.

Intinya, sepertinya SQL Server mungkin memiliki rencana eksekusi cache yang rusak. Anda mencapai rencana yang buruk dengan server web Anda, tetapi SSMS mendarat di paket yang berbeda karena ada pengaturan yang berbeda pada bendera ARITHABORT (yang sebaliknya tidak akan berdampak pada kueri / penyimpanan khusus Anda).

Lihat ADO.NET memanggil Prosedur Tersimpan T-SQL menyebabkan SqlTimeoutException untuk contoh lain, dengan penjelasan dan resolusi yang lebih lengkap.

StriplingWarrior
sumber
Ini adalah beberapa petunjuk menarik, akan membaca lebih lanjut tentang mereka dan kembali lagi dalam beberapa .. terima kasih.
iamserious
44
Hai, Bersihkan cache DBCC DROPCLEANBUFFERSdan DBCC FREEPROCCACHElakukan triknya! Saya menduga rencana eksekusi itu entah bagaimana rusak atau tidak diperbarui. Saya ragu itu perbaikan permanen, jika bisa korup sekali, bisa melakukannya lagi. Jadi saya masih mencari penyebab yang masuk akal. Karena aplikasi web sudah kembali berfungsi, saya tidak perlu merasakan tekanan. Terimakasih banyak.
iamserious
2
@iamserious - Anda berhasil terima kasih! Setelah mengubah prosedur saya yang tersimpan, saya mengalami masalah batas waktu. Kemudian saya menjalankan dua perintah DBCC yang Anda sebutkan di atas dan itu memecahkan masalah.
Induster
5
@Mangist: Karena ini adalah masalah yang rumit, tidak ada gunanya, tetapi Anda dapat mencoba menggunakan OPTIMIZE FORatau OPTION(Recompile)meminta petunjuk pada kueri yang menyebabkan masalah. Artikel ini membahas masalah tersebut secara mendalam. Jika Entity Framework menghasilkan kueri Anda, posting ini tampaknya menyediakan cara untuk menambahkan petunjuk kueri ke kueri Anda.
StriplingWarrior
8
Alih-alih menjalankan DBCC DROPCLEANBUFFERS dan DBCC FREEPROCCACHE yang akan menghapus SEMUA rencana eksekusi, Anda dapat membatalkan dan membuat ulang masalah yang tersimpan prosedur.
jnoreiga
51

Saya juga mengalami bahwa kueri berjalan lambat dari web dan cepat di SSMS dan saya akhirnya menemukan bahwa masalahnya adalah sesuatu yang disebut parameter sniffing.

Perbaikan bagi saya adalah mengubah semua parameter yang digunakan di sproc ke variabel lokal.

misalnya. perubahan:

ALTER PROCEDURE [dbo].[sproc] 
    @param1 int,
AS
SELECT * FROM Table WHERE ID = @param1 

untuk:

ALTER PROCEDURE [dbo].[sproc] 
    @param1 int,
AS
DECLARE @param1a int
SET @param1a = @param1
SELECT * FROM Table WHERE ID = @param1a

Kelihatannya aneh, tapi itu memperbaiki masalah saya.

Zane
sumber
3
Wow, saya memiliki masalah yang sama dan tidak percaya ini akan berhasil tetapi berhasil.
Lac Ho
1
Zane, apakah Anda tahu apakah ini akan memperbaiki masalah secara permanen atau dapat muncul kembali? Terima kasih!
Lac Ho
1
Sejak membuat perubahan ini, saya tidak mengalami masalah dengan kecepatan prosedur yang disimpan.
Zane
1
Wow, masalah saya juga sudah diperbaiki! Ini HARUS menjadi bug di server sql. Mengapa memaksa penulis sql untuk melompat melalui lingkaran konyol ini ketika MSSQL dapat secara internal mengkonversi ke lokal di bawah tenda, untuk mendapatkan kinerja yang BESAR?
HerrimanCoder
3
Mungkinkah mengubah proc menyebabkan rencana eksekusi baru dibuat, jadi solusinya tidak benar-benar menyalin variabel input ke variabel lokal tetapi hanya memaksa pembuatan rencana eksekusi baru (seperti yang dijelaskan dalam solusi yang diterima)?
Paus Garland
10

Bukan untuk mengirim spam, tetapi sebagai solusi yang semoga bermanfaat bagi orang lain, sistem kami mengalami tingkat waktu tunggu yang tinggi.

Saya mencoba mengatur prosedur yang disimpan untuk dikompilasi ulang dengan menggunakan sp_recompiledan ini menyelesaikan masalah untuk satu SP.

Pada akhirnya ada lebih banyak SP yang time-out, banyak di antaranya belum pernah melakukannya sebelumnya, dengan menggunakan DBCC DROPCLEANBUFFERSdan DBBC FREEPROCCACHEtingkat insiden timeout telah turun drastis secara signifikan - masih ada kejadian yang terisolasi, beberapa di mana saya menduga regenerasi rencana sedang berlangsung sementara, dan beberapa di mana SPs benar-benar berkinerja buruk dan perlu evaluasi ulang.

Clint
sumber
1
Terima kasih untuk ini. Menandai prosedur kompilasi ulang menggunakan perintah sp_recompile bekerja untuk saya ketika DBCC DROPCLEANBUFFERSdan DBCC FREEPROCCACHEtidak ada bedanya.
Steve
4

Mungkinkah beberapa panggilan DB lain yang dilakukan sebelum aplikasi web memanggil SP tetap membuka transaksi? Itu bisa menjadi alasan SP ini menunggu saat dipanggil oleh aplikasi web. Saya katakan isolasi panggilan di aplikasi web (letakkan di halaman baru) untuk memastikan bahwa beberapa tindakan sebelumnya di aplikasi web menyebabkan masalah ini.

Tundey
sumber
1
Hai @Tundey, saya mengisolasi panggilan dan masih membutuhkan waktu sekitar 30 detik dan waktu habis. jadi itu harus menjadi sesuatu antara komunikasi, kurasa?
iamserious
1

Cukup mengompilasi ulang prosedur yang tersimpan (fungsi tabel dalam kasus saya) berhasil untuk saya

Guy Biber
sumber
1

seperti @Zane mengatakan itu bisa jadi karena parameter sniffing. Saya mengalami perilaku yang sama dan saya melihat rencana pelaksanaan prosedur dan semua pernyataan sp berturut-turut (menyalin semua pernyataan dari prosedur, menyatakan parameter sebagai variabel dan memberikan nilai yang sama untuk variabel sebagai parameternya). Namun rencana eksekusi terlihat sangat berbeda. Eksekusi sp membutuhkan waktu 3-4 detik dan pernyataan berturut-turut dengan nilai yang persis sama langsung dikembalikan.

rencana eksekusi

Setelah googling beberapa saya menemukan bacaan menarik tentang perilaku itu: Lambat dalam Aplikasi, Cepat di SSMS?

Saat menyusun prosedur, SQL Server tidak mengetahui bahwa nilai @fromdate berubah, tetapi mengompilasi prosedur dengan asumsi bahwa @fromdate memiliki nilai NULL. Karena semua perbandingan dengan NULL menghasilkan UNKNOWN, kueri tidak dapat mengembalikan baris apa pun sama sekali, jika @fromdate masih memiliki nilai ini pada saat run-time. Jika SQL Server akan mengambil nilai input sebagai kebenaran akhir, itu bisa membuat rencana dengan hanya Pemindaian Konstan yang tidak mengakses tabel sama sekali (jalankan kueri SELECT * FROM Orders WHERE OrderDate> NULL untuk melihat contoh ini) . Tetapi SQL Server harus menghasilkan rencana yang mengembalikan hasil yang benar tidak peduli berapa nilai @fromdate pada saat run-time. Di sisi lain, tidak ada kewajiban untuk membangun rencana yang terbaik untuk semua nilai. Jadi, karena asumsinya adalah bahwa tidak ada baris yang akan dikembalikan, SQL Server menetapkan Pencarian Indeks.

Masalahnya adalah saya memiliki parameter yang dapat dibiarkan kosong dan jika dikirimkan sebagai null, parameter tersebut akan diinisialisasi dengan nilai default.

create procedure dbo.procedure
    @dateTo datetime = null
begin
    if (@dateTo is null)
    begin
        select @dateTo  = GETUTCDATE()
    end

    select foo
    from dbo.table
    where createdDate < @dateTo
end

Setelah saya mengubahnya menjadi

create procedure dbo.procedure
    @dateTo datetime = null
begin
    declare @to datetime = coalesce(@dateTo, getutcdate())

    select foo
    from dbo.table
    where createdDate < @to
end 

itu bekerja seperti pesona lagi.

TomPez
sumber